Она говорит, что функция принимает список пар со значениями типа k
и v
и возвращает отображение, которое отображает ключи типа k
в значения типа v
. Обратите внимание, что если мы реализуем ассоциативный список с помощью обычного списка, то значения ключей должны лишь уметь сравниваться (иметь экземпляр класса типов Eq
); теперь же должна быть возможность их упорядочить (класс типов Ord
). Это существенное ограничение модуля Data.Map
. Упорядочиваемые ключи нужны ему для того, чтобы размещать данные более эффективно.
Теперь мы можем преобразовать наш исходный ассоциативный список phoneBook
в отображение. Заодно добавим сигнатуру:
import qualified Data.Map as Map
phoneBook :: Map.Map String String
phoneBook = Map.fromList $
[("оля","555–29-38")
,("женя","452–29-28")
,("катя","493–29-28")
,("маша","205–29-28")
,("надя","939–82-82")
,("юля","853–24-92")
]
Отлично. Загрузим этот сценарий в GHCi и немного поиграем с телефонной книжкой. Во-первых, воспользуемся функцией lookup
и поищем какие-нибудь номера. Функция lookup
принимает ключ и отображение и пытается найти соответствующее ключу значение. Если всё прошло удачно, возвращается обёрнутое в Just
значение; в противном случае – Nothing
:
ghci> :t Map.lookup
Map.lookup :: (Ord k) => k -> Map.Map k a -> Maybe a
ghci> Map.lookup "оля" phoneBook
Just "555-29-38"
ghci> Map.lookup "надя" phoneBook
Just "939-82-82"
ghci> Map.lookup "таня" phoneBook
Nothing
Следующий трюк: создадим новое отображение, добавив в исходное новый номер. Функция insert
принимает ключ, значение и отображение и возвращает новое отображение – почти такое же, что и исходное, но с добавленными ключом и значением:
ghci> :t Map.insert
Map.insert :: (Ord k) => k -> a -> Map.Map k a -> Map.Map k a
ghci> Map.lookup "таня" phoneBook
Nothing
ghci> let newBook = Map.insert "таня" "341-90-21" phoneBook
ghci> Map.lookup "таня" newBook
Just "341-90-21"
Давайте посчитаем, сколько у нас телефонных номеров. Для этого нам понадобится функция size
из модуля Data.Map
. Она принимает отображение и возвращает его размер. Тут всё ясно:
ghci> :t Map.size
Map.size :: Map.Map k a -> Int
ghci> Map.size phoneBook
6
ghci> Map.size newBook
7
Номера в нашей телефонной книжке представлены строками. Допустим, мы хотим вместо них использовать списки цифр: то есть вместо номера "939-82-82"
– список [9,3,9,8,2,8,2]
. Сначала напишем функцию, конвертирующую телефонный номер в строке в список целых. Можно попытаться применить функцию digitToInt
из модуля Data.Char
к каждому символу в строке, но она не знает, что делать с дефисом! Поэтому нужно избавиться от всех нецифр. Попросим помощи у функции isDigit
из модуля Data.Char
, которая принимает символ и сообщает нам, является ли он цифрой. Как только строка будет отфильтрована, пройдёмся по ней функцией digitToInt
.
string2digits :: String -> [Int]
string2digits = map digitToInt . filter isDigit
Да, не забудьте импортировать модуль Data.Char
. Пробуем:
ghci> string2digits "948-92-82"
[9,4,8,9,2,8,2]
Замечательно! Теперь применим функцию map
из модуля Data. Map
, чтобы пропустить функцию string2digits
по элементам отображения phoneBook
:
ghci> let intBook = Map.Map string2digits phoneBook
ghci> :t intBook
intBook :: Map.Map String [Int]
ghci> Map.lookup "оля" intBook
Just [5,5,5,2,9,3,8]
Функция map
из модуля Data.Map
принимает функцию и отображение и применяет эту функцию к каждому значению в отображении.
Расширим телефонную книжку. Предположим, что у кого-нибудь есть несколько телефонных номеров, и наш ассоциативный список выглядит как-то так:
phoneBook =
[("оля","555–29-38")
,("оля","342–24-92")
,("женя","452–29-28")
,("катя","493–29-28")
,("катя","943–29-29")
,("катя","827–91-62")
,("маша","205–29-28")
,("надя","939–82-82")