- Любовные романы
- Фантастика и фэнтези
- Ненаучная фантастика
- Ироническое фэнтези
- Научная Фантастика
- Фэнтези
- Ужасы и Мистика
- Боевая фантастика
- Альтернативная история
- Космическая фантастика
- Попаданцы
- Юмористическая фантастика
- Героическая фантастика
- Детективная фантастика
- Социально-психологическая
- Боевое фэнтези
- Русское фэнтези
- Киберпанк
- Романтическая фантастика
- Городская фантастика
- Технофэнтези
- Мистика
- Разная фантастика
- Иностранное фэнтези
- Историческое фэнтези
- LitRPG
- Эпическая фантастика
- Зарубежная фантастика
- Городское фентези
- Космоопера
- Разное фэнтези
- Книги магов
- Любовное фэнтези
- Постапокалипсис
- Бизнес
- Историческая фантастика
- Социально-философская фантастика
- Сказочная фантастика
- Стимпанк
- Романтическое фэнтези
- Ироническая фантастика
- Детективы и Триллеры
- Проза
- Юмор
- Феерия
- Новелла
- Русская классическая проза
- Современная проза
- Повести
- Контркультура
- Русская современная проза
- Историческая проза
- Проза
- Классическая проза
- Советская классическая проза
- О войне
- Зарубежная современная проза
- Рассказы
- Зарубежная классика
- Очерки
- Антисоветская литература
- Магический реализм
- Разное
- Сентиментальная проза
- Афоризмы
- Эссе
- Эпистолярная проза
- Семейный роман/Семейная сага
- Поэзия, Драматургия
- Приключения
- Детская литература
- Загадки
- Книга-игра
- Детская проза
- Детские приключения
- Сказка
- Прочая детская литература
- Детская фантастика
- Детские стихи
- Детская образовательная литература
- Детские остросюжетные
- Учебная литература
- Зарубежные детские книги
- Детский фольклор
- Буквари
- Книги для подростков
- Школьные учебники
- Внеклассное чтение
- Книги для дошкольников
- Детская познавательная и развивающая литература
- Детские детективы
- Домоводство, Дом и семья
- Юмор
- Документальные книги
- Бизнес
- Работа с клиентами
- Тайм-менеджмент
- Кадровый менеджмент
- Экономика
- Менеджмент и кадры
- Управление, подбор персонала
- О бизнесе популярно
- Интернет-бизнес
- Личные финансы
- Делопроизводство, офис
- Маркетинг, PR, реклама
- Поиск работы
- Бизнес
- Банковское дело
- Малый бизнес
- Ценные бумаги и инвестиции
- Краткое содержание
- Бухучет и аудит
- Ораторское искусство / риторика
- Корпоративная культура, бизнес
- Финансы
- Государственное и муниципальное управление
- Менеджмент
- Зарубежная деловая литература
- Продажи
- Переговоры
- Личная эффективность
- Торговля
- Научные и научно-популярные книги
- Биофизика
- География
- Экология
- Биохимия
- Рефераты
- Культурология
- Техническая литература
- История
- Психология
- Медицина
- Прочая научная литература
- Юриспруденция
- Биология
- Политика
- Литературоведение
- Религиоведение
- Научпоп
- Психология, личное
- Математика
- Психотерапия
- Социология
- Воспитание детей, педагогика
- Языкознание
- Беременность, ожидание детей
- Транспорт, военная техника
- Детская психология
- Науки: разное
- Педагогика
- Зарубежная психология
- Иностранные языки
- Филология
- Радиотехника
- Деловая литература
- Физика
- Альтернативная медицина
- Химия
- Государство и право
- Обществознание
- Образовательная литература
- Учебники
- Зоология
- Архитектура
- Науки о космосе
- Ботаника
- Астрология
- Ветеринария
- История Европы
- География
- Зарубежная публицистика
- О животных
- Шпаргалки
- Разная литература
- Зарубежная литература о культуре и искусстве
- Пословицы, поговорки
- Боевые искусства
- Прочее
- Периодические издания
- Фанфик
- Военное
- Цитаты из афоризмов
- Гиды, путеводители
- Литература 19 века
- Зарубежная образовательная литература
- Военная история
- Кино
- Современная литература
- Военная техника, оружие
- Культура и искусство
- Музыка, музыканты
- Газеты и журналы
- Современная зарубежная литература
- Визуальные искусства
- Отраслевые издания
- Шахматы
- Недвижимость
- Великолепные истории
- Музыка, танцы
- Авто и ПДД
- Изобразительное искусство, фотография
- Истории из жизни
- Готические новеллы
- Начинающие авторы
- Спецслужбы
- Подростковая литература
- Зарубежная прикладная литература
- Религия и духовность
- Старинная литература
- Справочная литература
- Компьютеры и Интернет
- Блог
Изучай Haskell во имя добра! - Миран Липовача
Шрифт:
Интервал:
Закладка:
findKey :: (Eq k) => k –> [(k,v)] –> Maybe v
findKey key [] = Nothing
findKey key ((k,v):xs)
| key == k = Just v
| otherwise = findKey key xs
Посмотрите на декларацию типа. Функция принимает ключ, который можно проверить на равенство (Eq), и ассоциативный список, а затем, возможно, возвращает значение. Выглядит правдоподобно.
Это классическая рекурсивная функция, обрабатывающая список. Базовый случай, разбиение списка на «голову» и «хвост», рекурсивный вызов – всё на месте. Также это классический шаблон для применения свёртки. Посмотрим, как то же самое можно реализовать с помощью свёртки.
findKey :: (Eq k) => k –> [(k,v)] –> Maybe v
findKey key = foldr ((k,v) acc –> if key == k then Just v else acc) Nothing
ПРИМЕЧАНИЕ. Как правило, лучше использовать свёртки для подобных стандартных рекурсивных обходов списка вместо явного описания рекурсивной функции, потому что свёртки легче читаются и понимаются. Любой человек догадается, что это свёртка, как только увидит вызов функции foldr – однако потребуется больше интеллектуальных усилий для того, чтобы распознать явно написанную рекурсию.
ghci> findKey "юля" phoneBook
Just "853–24-92"
ghci> findKey "оля" phoneBook
Just "555–29-38"
ghci> findKey "аня" phoneBook
Nothing
Отлично, работает! Если у нас есть телефонный номер девушки, мы просто (Just) получим номер; в противном случае не получим ничего (Nothing).
Модуль Data.Map
Мы только что реализовали функцию lookup из модуля Data.List. Если нам нужно значение, соответствующее ключу, понадобится обойти все элементы списка, пока мы его не найдём.
Модуль Data.Map предлагает ассоциативные списки, которые работают намного быстрее (поскольку они реализованы с помощью деревьев), а также множество дополнительных функций. Начиная с этого момента мы будем говорить, что работаем с отображениями вместо ассоциативных списков.
Так как модуль Data.Map экспортирует функции, конфликтующие с модулями Prelude и Data.List, мы будем импортировать их с помощью квалифицированного импорта.
import qualified Data.Map as Map
Поместите этот оператор в исходный код и загрузите его в GHCi. Мы будем преобразовывать ассоциативный список в отображение с помощью функции fromList из модуля Data.Map. Функция fromList принимает ассоциативный список (в форме списка) и возвращает отображение с теми же ассоциациями. Немного поиграем:
ghci> Map.fromList [(3, "туфли"),(4,"деревья"),(9,"пчёлы")]
fromList [(3, "туфли"),(4,"деревья"),(9,"пчёлы")]
ghci> Map.fromList [("эрик","форман"),("роберт","чейз"),("крис", "тауб")]
fromList [("крис","тауб"),("роберт","чейз"),("эрик","форман")]
Когда отображение из модуля Data.Map показывается в консоли, сначала выводится fromList, а затем ассоциативный список, представляющий отображение.
Если в исходном списке есть дубликаты ключей, они отбрасываются:
ghci> Map.fromList [("MS",1),("MS",2),("MS",3)]
fromList [("MS",3)]
Вот сигнатура функции fromList:
Map.fromList :: (Ord k) => [(k, v)] –> Map.Map k v
Она говорит, что функция принимает список пар со значениями типа 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")
,("юля","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:
ghci> Map.lookup "катя" $ phoneBookToMap phoneBook
["827–91-62","943–29-29","493–29-28"]

