из модуля
>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")
> ,("юля","853–24-92")
> ,("юля","555–21-11")
> ]
Если мы просто вызовем >fromList
, чтобы поместить всё это в отображение, то потеряем массу номеров! Вместо этого воспользуемся другой функцией из модуля >Data.Map
, а именно функцией >fromListWith
. Эта функция действует почти как >fromList
, но вместо отбрасывания повторяющихся ключей вызывает переданную ей функцию, которая и решает, что делать.
>phoneBookToMap :: (Ord k) => [(k, String)] -> Map.Map k String
>phoneBookToMap xs = Map.fromListWith add xs
> where add number1 number2 = number1 ++ ", " ++ number2
Если функция >fromListWith
обнаруживает, что ключ уже существует, она вызывает переданную ей функцию, которая соединяет оба значения в одно, а затем заменяет старое значение на новое, полученное от соединяющей функции:
>ghci> Map.lookup "катя" $ phoneBookToMap phoneBook
>"827–91-62, 943–29-29, 493–29-28"
>ghci> Map.lookup "надя" $ phoneBookToMap phoneBook
>"939-82-82"
>ghci> Map.lookup "оля" $ phoneBookToMap phoneBook
>"342-24-92, 555-29-38"
А ещё можно было бы сделать все значения в ассоциативном списке одноэлементными списками, а потом скомбинировать их операцией >++
, например:
>phoneBookToMap :: (Ord k) => [(k, a)] -> Map.Map k [a]
>phoneBookToMap xs = Map.fromListWith (++) $ map (\(k,v) -> (k, [v])) xs
Проверим в GHCi: