Миран Липовача - Изучай Haskell во имя добра!
- Название:Изучай Haskell во имя добра!
- Автор:
- Жанр:
- Издательство:ДМК Пресс
- Год:2012
- Город:Москва
- ISBN:978-5-94074-749-9
- Рейтинг:
- Избранное:Добавить в избранное
-
Отзывы:
-
Ваша оценка:
Миран Липовача - Изучай Haskell во имя добра! краткое содержание
Язык Haskell имеет множество впечатляющих возможностей, но главное его свойство в том, что меняется не только способ написания кода, но и сам способ размышления о проблемах и возможных решениях. Этим Haskell действительно отличается от большинства языков программирования. С его помощью мир можно представить и описать нестандартным образом. И поскольку Haskell предлагает совершенно новые способы размышления о проблемах, изучение этого языка может изменить и стиль программирования на всех прочих.
Ещё одно необычное свойство Haskell состоит в том, что в этом языке придаётся особое значение рассуждениям о типах данных. Как следствие, вы помещаете больше внимания и меньше кода в ваши программы.
Вне зависимости от того, в каком направлении вы намерены двигаться, путешествуя в мире программирования, небольшой заход в страну Haskell себя оправдает. А если вы решите там остаться, то наверняка найдёте чем заняться и чему поучиться!
Эта книга поможет многим читателям найти свой путь к Haskell.
Отображения, монады, моноиды и другое! Всё сказано в названии: «Изучай Хаскель во имя добра!» – весёлый иллюстрированный самоучитель по этому сложному функциональному языку.
С помощью оригинальных рисунков автора, отсылке к поп-культуре, и, самое главное, благодаря полезным примерам кода, эта книга обучает основам функционального программирования так, как вы никогда не смогли бы себе представить.
Вы начнете изучение с простого материала: основы синтаксиса, рекурсия, типы и классы типов. Затем, когда вы преуспеете в основах, начнется настоящий мастер-класс от профессионала: вы изучите, как использовать аппликативные функторы, монады, застежки, и другие легендарные конструкции Хаскеля, о которых вы читали только в сказках.
Продираясь сквозь образные (и порой безумные) примеры автора, вы научитесь:
• Смеяться в лицо побочным эффектам, поскольку вы овладеете техниками чистого функционального программирования.
• Использовать волшебство «ленивости» Хаскеля для игры с бесконечными наборами данных.
• Организовывать свои программы, создавая собственные типы, классы типов и модули.
• Использовать элегантную систему ввода-вывода Хаскеля, чтобы делиться гениальностью ваших программ с окружающим миром.
Нет лучшего способа изучить этот мощный язык, чем чтение «Изучай Хаскель во имя добра!», кроме, разве что, поедания мозга его создателей. Миран Липовача (Miran Lipovača) изучает информатику в Любляне (Словения). Помимо его любви к Хаскелю, ему нравится заниматься боксом, играть на бас-гитаре и, конечно же, рисовать. У него есть увлечение танцующими скелетами и числом 71, а когда он проходит через автоматические двери, он притворяется, что на самом деле открывает их силой своей мысли.
Изучай Haskell во имя добра! - читать онлайн бесплатно ознакомительный отрывок
Интервал:
Закладка:
Значение аккумулятора (и, следовательно, результат) функции foldr
могут быть любого типа. Это может быть число, булевское значение или даже список. Мы реализуем функцию map
с помощью правой свёртки. Аккумулятор будет списком; будем накапливать пересчитанные элементы один за другим. Очевидно, что начальным элементом является пустой список:
map' :: (a –> b) –> [a] –> [b]
map' f xs = foldr (\x acc –> f x : acc) [] xs
Если мы применяем функцию (+3)
к списку [1,2,3]
, то обрабатываем список справа. Мы берём последний элемент, тройку, применяем к нему функцию, и результат оказывается равен 6
. Затем добавляем это число к аккумулятору, который был равен []
. 6:[]
– то же, что и [6]
; это новое значение аккумулятора. Мы применяем функцию (+3)
к значению 2
, получаем 5
и при помощи конструктора списка :
добавляем его к аккумулятору, который становится равен [5,6]
. Применяем функцию (+3)
к значению 1
, добавляем результат к аккумулятору и получаем финальное значение [4,5,6]
.
Конечно, можно было бы реализовать эту функцию и при помощи левой свёртки:
map' :: (a -> b) -> [a] -> [b]
map' f xs = foldl (\acc x –> acc ++ [f x]) [] xs
Но операция конкатенации ++
значительно дороже, чем конструктор списка :
, так что мы обычно используем правую свёртку, когда строим списки из списков.
Если вы обратите список задом наперёд, то сможете выполнять правую свёртку с тем же результатом, что даёт левая свёртка, и наоборот. В некоторых случаях обращать список не требуется. Функцию sum
можно реализовать как с помощью левой, так и с помощью правой свёртки. Единственное серьёзное отличие: правые свёртки работают на бесконечных списках, а левые – нет! Оно и понятно: если вы берёте бесконечный список в некоторой точке и затем сворачиваете его справа, рано или поздно вы достигаете начала списка. Если же вы берёте бесконечный список в некоторой точке и пытаетесь свернуть его слева, вы никогда не достигнете конца!

Свёртки могут быть использованы для реализации любой функции, где вы вычисляете что-либо за один обход списка [8] Это так. В качестве упражнения повышенной сложности читателю рекомендуется реализовать при помощи свёртки функции drop и dropWhile из стандартной библиотеки. – Прим. ред.
. Если вам нужно обойти список для того, чтобы что-либо вычислить, скорее всего, вам нужна свёртка. Вот почему свёртки, наряду с функциями map
и filter
, – одни из наиболее часто используемых функций в функциональном программировании.
Функции foldl1 и foldr1
Функции foldl1
и foldr1
работают примерно так же, как и функции foldl
и foldr
, только нет необходимости явно задавать стартовое значение. Они предполагают, что первый (или последний) элемент списка является стартовым элементом, и затем начинают свёртку со следующим элементом. Принимая это во внимание, функцию maximum
можно реализовать следующим образом:
maximum' :: (Ord a) => [a] -> a
maximum' = foldl1 max
Мы реализовали функцию maximum
, используя foldl1
. Вместо использования начального значения функция foldl1
предполагает, что таковым является первый элемент списка, после чего перемещается к следующему. Поэтому всё, что ей необходимо, – это бинарная функция и сворачиваемый лист! Мы начинаем с «головы» списка и сравниваем каждый элемент с аккумулятором. Если элемент больше аккумулятора, мы сохраняем его в качестве нового значения аккумулятора; в противном случае сохраняем старое. Мы передаём функцию max
в качестве параметра foldl1
, поскольку она ровно это и делает: берёт два значения и возвращает большее. К моменту завершения свёртки останется самый большой элемент.
По скольку эти функции требуют, чтобы сворачиваемые списки имели хотя бы один элемент, то, если вызвать их с пустым списком, произойдёт ошибка времени выполнения.
С другой стороны, функции foldl
и foldr
хорошо работают с пустыми списками. Подумайте, имеет ли смысл свёртка для пустых списков в вашем контексте. Если функция не имеет смысла для пустого списка, то, возможно, вы захотите использовать функции foldl1
или foldr1
для её реализации.
Примеры свёрток
Для того чтобы показать, насколько мощны свёртки, мы собираемся реализовать с их помощью несколько стандартных библиотечных функций. Во-первых, реализуем свою версию функции reverse
:
reverse' :: [a] -> [a]
reverse' = foldl (\acc x -> x : acc) []
Здесь мы обращаем список, пользуясь пустым списком как начальным значением аккумулятора, и, обходя затем исходный список слева, добавляем текущий элемент в начало аккумулятора.
Функция \acc x -> x : acc
– почти то же, что и операция :
, за исключением порядка следования параметров. Поэтому функцию reverse'
можно переписать и так:
reverse' :: [a] -> [a]
reverse' = foldl (flip (:)) []
Теперь реализуем функцию product
:
product' :: (Num a) => [a] -> a
product' = foldl (*) 1
Чтобы вычислить произведение всех элементов списка, следует начать с аккумулятора равного 1
. Затем мы выполняем свёртку функцией (*)
, которая перемножает каждый элемент списка на аккумулятор.
Вот реализация функции filter
:
filter' :: (a -> Bool) -> [a] -> [a]
filter' p = foldr (\x acc -> if p x then x : acc else acc) []
Здесь начальное значение аккумулятора является пустым списком. Мы сворачиваем список справа налево и проверяем каждый элемент, пользуясь предикатом p
. Если p x
возвращает истину, элемент x
помещается в начало аккумулятора. В противном случае аккумулятор остаётся без изменения.
Напоследок реализуем функцию last
:
last' :: [a] -> a
last' = foldl1 (\ x -> x)
Для получения последнего элемента списка мы применяем foldr1
. Начинаем с «головы» списка, а затем применяем бинарную функцию, которая игнорирует аккумулятор и устанавливает текущий элемент списка как новое значение аккумулятора. Как только мы достигаем конца списка, аккумулятор — то есть последний элемент – возвращается в качестве результата свёртки.
Иной взгляд на свёртки
Есть ещё один способ представить работу правой и левой свёртки. Скажем, мы выполняем правую свёртку с бинарной функцией f
и стартовым значением z
. Если мы применяем правую свёртку к списку [3,4,5,6]
, то на самом деле вычисляем вот что:
f 3 (f 4 (f 5 (f 6 z)))
Функция f
вызывается с последним элементом в списке и аккумулятором; получившееся значение передаётся в качестве аккумулятора при вызове функции с предыдущим значением, и т. д. Если мы примем функцию f
за операцию сложения и начальное значение за нуль, наш пример преобразуется так:
Интервал:
Закладка: