Изучай Haskell во имя добра! (Липовача) - страница 96

.

Например, в школе есть шкафчики для того, чтобы ученикам было куда клеить постеры Guns’n’Roses. Каждый шкафчик открывается кодовой комбинацией. Если школьнику понадобился шкафчик, он говорит администратору, шкафчик под каким номером ему нравится, и администратор выдаёт ему код. Если этот шкафчик уже кем-либо используется, администратор не сообщает код – они вместе с учеником должны будут выбрать другой вариант. Будем использовать модуль >Data.Map для того, чтобы хранить информацию о шкафчиках. Это будет отображение из номера шкафчика в пару, где первый компонент указывает, используется шкафчик или нет, а второй компонент – код шкафчика.

>import qualified Data.Map as Map


>data LockerState = Taken | Free deriving (Show, Eq)


>type Code = String


>type LockerMap = Map.Map Int (LockerState, Code)

Довольно просто. Мы объявляем новый тип данных для хранения информации о том, был шкафчик занят или нет. Также мы создаём синоним для кода шкафчика и для типа, который отображает целые числа в пары из статуса шкафчика и кода. Теперь создадим функцию для поиска кода по номеру. Мы будем использовать тип >Either String Code для представления результата, так как поиск может не удаться по двум причинам – шкафчик уже занят, в этом случае нельзя сообщать код, или номер шкафчика не найден вообще. Если поиск не удался, возвращаем значение типа >String с пояснениями.

>lockerLookup :: Int –> LockerMap –> Either String Code

>lockerLookup lockerNumber map =

>  case Map.lookup lockerNumber map of

>   Nothing –> Left $ "Шкафчик № " ++ show lockerNumber ++

>                     " не существует!"


>   Just (state, code) –>

>      if state /= Taken

>        then Right code

>        else Left $ "Шкафчик № " ++ show lockerNumber ++ " уже занят!"

Мы делаем обычный поиск по отображению. Если мы получили значение >Nothing, то вернём значение типа >Left String, говорящее, что такой номер не существует. Если мы нашли номер, делаем дополнительную проверку, занят ли шкафчик. Если он занят, возвращаем значение >Left, говорящее, что шкафчик занят. Если он не занят, возвращаем значение типа >Right Code, в котором даём студенту код шкафчика. На самом деле это >Right String, но мы создали синоним типа, чтобы сделать наши объявления более понятными. Вот пример отображения:

>lockers :: LockerMap lockers = Map.fromList

>  [(100,(Taken,"ZD39I"))

>  ,(101,(Free,"JAH3I"))

>  ,(103,(Free,"IQSA9"))

>  ,(105,(Free,"QOTSA"))

>  ,(109,(Taken,"893JJ"))

>  ,(110,(Taken,"99292"))

>  ]

Давайте попытаемся узнать несколько кодов.

>ghci> lockerLookup 101 lockers

>Right "JAH3I"