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

из модуля >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: