Миран Липовача - Изучай Haskell во имя добра!
- Название:Изучай Haskell во имя добра!
- Автор:
- Жанр:
- Издательство:ДМК Пресс
- Год:2012
- Город:Москва
- ISBN:978-5-94074-749-9
- Рейтинг:
- Избранное:Добавить в избранное
-
Отзывы:
-
Ваша оценка:
Миран Липовача - Изучай Haskell во имя добра! краткое содержание
Язык Haskell имеет множество впечатляющих возможностей, но главное его свойство в том, что меняется не только способ написания кода, но и сам способ размышления о проблемах и возможных решениях. Этим Haskell действительно отличается от большинства языков программирования. С его помощью мир можно представить и описать нестандартным образом. И поскольку Haskell предлагает совершенно новые способы размышления о проблемах, изучение этого языка может изменить и стиль программирования на всех прочих.
Ещё одно необычное свойство Haskell состоит в том, что в этом языке придаётся особое значение рассуждениям о типах данных. Как следствие, вы помещаете больше внимания и меньше кода в ваши программы.
Вне зависимости от того, в каком направлении вы намерены двигаться, путешествуя в мире программирования, небольшой заход в страну Haskell себя оправдает. А если вы решите там остаться, то наверняка найдёте чем заняться и чему поучиться!
Эта книга поможет многим читателям найти свой путь к Haskell.
Отображения, монады, моноиды и другое! Всё сказано в названии: «Изучай Хаскель во имя добра!» – весёлый иллюстрированный самоучитель по этому сложному функциональному языку.
С помощью оригинальных рисунков автора, отсылке к поп-культуре, и, самое главное, благодаря полезным примерам кода, эта книга обучает основам функционального программирования так, как вы никогда не смогли бы себе представить.
Вы начнете изучение с простого материала: основы синтаксиса, рекурсия, типы и классы типов. Затем, когда вы преуспеете в основах, начнется настоящий мастер-класс от профессионала: вы изучите, как использовать аппликативные функторы, монады, застежки, и другие легендарные конструкции Хаскеля, о которых вы читали только в сказках.
Продираясь сквозь образные (и порой безумные) примеры автора, вы научитесь:
• Смеяться в лицо побочным эффектам, поскольку вы овладеете техниками чистого функционального программирования.
• Использовать волшебство «ленивости» Хаскеля для игры с бесконечными наборами данных.
• Организовывать свои программы, создавая собственные типы, классы типов и модули.
• Использовать элегантную систему ввода-вывода Хаскеля, чтобы делиться гениальностью ваших программ с окружающим миром.
Нет лучшего способа изучить этот мощный язык, чем чтение «Изучай Хаскель во имя добра!», кроме, разве что, поедания мозга его создателей. Миран Липовача (Miran Lipovača) изучает информатику в Любляне (Словения). Помимо его любви к Хаскелю, ему нравится заниматься боксом, играть на бас-гитаре и, конечно же, рисовать. У него есть увлечение танцующими скелетами и числом 71, а когда он проходит через автоматические двери, он притворяется, что на самом деле открывает их силой своей мысли.
Изучай Haskell во имя добра! - читать онлайн бесплатно ознакомительный отрывок
Интервал:
Закладка:
ghci> Nothing
Nothing
ghci> Just "эй"
Just "эй"
ghci> Just 3
Just 3
ghci> :t Just "эй"
Just "эй" :: Maybe [Char]
ghci> :t Just True
Just True :: Maybe Bool
Видите, значение Just True
имеет тип Maybe Bool
. Похоже на то, что список, содержащий значения типа Bool
, имеет тип [Bool]
.
Если функция find
находит элемент, удовлетворяющий предикату, она возвращает этот элемент, обёрнутый в Just
. Если не находит, возвращает Nothing
:
ghci> find (>4) [3,4,5,6,7]
Just 5
ghci> find odd [2,4,6,8,9]
Just 9
ghci> find (=='x') "меч-кладенец"
Nothing
Вернёмся теперь к нашей задаче. Мы уже написали функцию digitSum
и знаем, как она работает, так что пришла пора собрать всё вместе. Напомню, что мы хотим найти число, сумма цифр которого равна 40.
firstTo40 :: Maybe Int
firstTo40 = find (\x -> digitSum == 40) [1..]
Мы просто взяли бесконечный список [1..]
и начали искать первое число, значение digitSum
для которого равно 40.
ghci> firstTo40
Just 49999
А вот и ответ! Можно сделать более общую функцию, которой нужно передавать искомую сумму в качестве параметра:
firstTo :: Int -> Maybe Int
firstTo n = find (\x -> digitSum x == n) [1..]
И небольшая проверка:
ghci> firstTo 27
Just 999
ghci> firstTo 1
Just 1
ghci> firstTo 13
Just 49
Отображение ключей на значения
Зачастую, работая с данными из некоторого набора, мы совершенно не заботимся, в каком порядке они расположены. Мы просто хотим получить к ним доступ по некоторому ключу. Например, желая узнать, кто живёт по известному адресу, мы ищем имена тех, кто по этому адресу проживает. В общем случае мы говорим, что ищем значение (чьё-либо имя) по ключу (адрес этого человека).
Почти хорошо: ассоциативные списки
Существует много способов построить отображение «ключ–значение». Один из них – ассоциативные списки. Ассоциативные списки (также называемые словарями или отображениями ) – это списки, которые хранят неупорядоченные пары «ключ–значение». Например, мы можем применять ассоциативные списки для хранения телефонных номеров, используя телефонный номер как значение и имя человека как ключ. Нам неважно, в каком порядке они сохранены: всё, что нам требуется, – получить телефонный номер по имени. Наиболее простой способ представить ассоциативный список в языке Haskell – использовать список пар. Первый компонент пары будет ключом, второй – значением. Вот пример ассоциативного списка с номерами телефонов:
phoneBook =
[("оля","555–29-38")
,("женя","452–29-28")
,("катя","493–29-28")
,("маша","205–29-28")
,("надя","939–82-82")
,("юля","853–24-92")
]
За исключением странного выравнивания, это просто список, состоящий из пар строк. Самая частая задача при использовании ассоциативных списков – поиск некоторого значения по ключу. Давайте напишем функцию для этой задачи.
findKey :: (Eq k) => k –> [(k,v)] –> v
findKey key xs = snd . head $ filter (\(k,v) –> key == k) xs
Всё довольно просто. Функция принимает ключ и список, фильтрует список так, что остаются только совпадающие ключи, получает первую пару «ключ–значение», возвращает значение. Но что произойдёт, если искомого ключа нет в списке? В этом случае мы будем пытаться получить «голову» пустого списка, что вызовет ошибку времени выполнения. Однако следует стремиться к тому, чтобы наши программы были более устойчивыми к «падениям», поэтому давайте используем тип Maybe
. Если мы не найдём ключа, то вернём значение Nothing
. Если найдём, будем возвращать Just < то, что нашли >
.
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
, а затем ассоциативный список, представляющий отображение.
Если в исходном списке есть дубликаты ключей, они отбрасываются:
Читать дальшеИнтервал:
Закладка: