Миран Липовача - Изучай Haskell во имя добра!
- Название:Изучай Haskell во имя добра!
- Автор:
- Жанр:
- Издательство:ДМК Пресс
- Год:2012
- Город:Москва
- ISBN:978-5-94074-749-9
- Рейтинг:
- Избранное:Добавить в избранное
-
Отзывы:
-
Ваша оценка:
Миран Липовача - Изучай Haskell во имя добра! краткое содержание
Язык Haskell имеет множество впечатляющих возможностей, но главное его свойство в том, что меняется не только способ написания кода, но и сам способ размышления о проблемах и возможных решениях. Этим Haskell действительно отличается от большинства языков программирования. С его помощью мир можно представить и описать нестандартным образом. И поскольку Haskell предлагает совершенно новые способы размышления о проблемах, изучение этого языка может изменить и стиль программирования на всех прочих.
Ещё одно необычное свойство Haskell состоит в том, что в этом языке придаётся особое значение рассуждениям о типах данных. Как следствие, вы помещаете больше внимания и меньше кода в ваши программы.
Вне зависимости от того, в каком направлении вы намерены двигаться, путешествуя в мире программирования, небольшой заход в страну Haskell себя оправдает. А если вы решите там остаться, то наверняка найдёте чем заняться и чему поучиться!
Эта книга поможет многим читателям найти свой путь к Haskell.
Отображения, монады, моноиды и другое! Всё сказано в названии: «Изучай Хаскель во имя добра!» – весёлый иллюстрированный самоучитель по этому сложному функциональному языку.
С помощью оригинальных рисунков автора, отсылке к поп-культуре, и, самое главное, благодаря полезным примерам кода, эта книга обучает основам функционального программирования так, как вы никогда не смогли бы себе представить.
Вы начнете изучение с простого материала: основы синтаксиса, рекурсия, типы и классы типов. Затем, когда вы преуспеете в основах, начнется настоящий мастер-класс от профессионала: вы изучите, как использовать аппликативные функторы, монады, застежки, и другие легендарные конструкции Хаскеля, о которых вы читали только в сказках.
Продираясь сквозь образные (и порой безумные) примеры автора, вы научитесь:
• Смеяться в лицо побочным эффектам, поскольку вы овладеете техниками чистого функционального программирования.
• Использовать волшебство «ленивости» Хаскеля для игры с бесконечными наборами данных.
• Организовывать свои программы, создавая собственные типы, классы типов и модули.
• Использовать элегантную систему ввода-вывода Хаскеля, чтобы делиться гениальностью ваших программ с окружающим миром.
Нет лучшего способа изучить этот мощный язык, чем чтение «Изучай Хаскель во имя добра!», кроме, разве что, поедания мозга его создателей. Миран Липовача (Miran Lipovača) изучает информатику в Любляне (Словения). Помимо его любви к Хаскелю, ему нравится заниматься боксом, играть на бас-гитаре и, конечно же, рисовать. У него есть увлечение танцующими скелетами и числом 71, а когда он проходит через автоматические двери, он притворяется, что на самом деле открывает их силой своей мысли.
Изучай Haskell во имя добра! - читать онлайн бесплатно ознакомительный отрывок
Интервал:
Закладка:
Посмотрим ещё на один пример:
ghci> sum (filter (> 10) (map (*2) [2..10]))
80
Очень много скобок, даже как-то уродливо. Поскольку оператор $ правоассоциативен, выражение f (g (z x))
эквивалентно записи f $ g $ z x
. Поэтому пример можно переписать:
sum $ filter (> 10) $ map (*2) [2..10]
Но кроме избавления от скобок оператор $
означает, что само применение функции может использоваться как и любая другая функция. Таким образом, мы можем, например, применить функцию к списку функций:
ghci> map ($ 3) [(4+), (10*), ( 2), sqrt]
[7.0,30.0,9.0,1.7320508075688772]
Функция ($ 3)
применяется к каждому элементу списка. Если задуматься о том, что она делает, то окажется, что она берёт функцию и применяет её к числу 3
. Поэтому в данном примере каждая функция из списка применится к тройке, что, впрочем, и так очевидно.
Композиция функций

В математике композиция функций определяется следующим образом:
( f ° g )( x ) = f ( g ( x ))
Это значит, что композиция двух функций создаёт новую функцию, которая, когда её вызывают, скажем, с параметром x , эквивалентна вызову g с параметром x , а затем вызову f с результатом первого вызова в качестве своего параметра.
В языке Haskell композиция функций понимается точно так же. Мы создаём её при помощи оператора (.)
, который определён следующим образом:
(.) :: (b –> c) –> (a –> b) –> a –> c
f . g = \x –> f (g x)
По декларации типа функция f
должна принимать параметр того же типа, что и результат функции g
. Таким образом, результирующая функция принимает параметр того же типа, что и функция g
, и возвращает значение того же типа, что и функция f
. Выражение negate . (* 3)
возвращает функцию, которая принимает число, умножает его на три и меняет его знак на противоположный.
Одно из применений композиции функций – это создание функций «на лету» для передачи их другим функциям в качестве параметров. Конечно, мы можем использовать для этого анонимные функции, но зачастую композиция функций понятнее и лаконичнее. Допустим, что у нас есть список чисел и мы хотим сделать их отрицательными. Один из способов сделать это – получить абсолютное значение числа (модуль), а затем перевести его в отрицательное, вот так:
ghci> map (\x –> negate (abs x)) [5,–3,–6,7,–3,2,–19,24]
[–5,–3,–6,–7,–3,–2,–19,–24]
Обратите внимание на анонимную функцию и на то, как она похожа на результирующую композицию функций. А вот что выйдет, если мы воспользуемся композицией:
ghci> map (negate . abs) [5,–3,–6,7,–3,2,–19,24]
[–5,–3,–6,–7,–3,–2,–19,–24]
Невероятно! Композиция функций правоассоциативна, поэтому у нас есть возможность включать в неё много функций за один раз. Выражение f (g (z x))
эквивалентно (f . g . z) x
. Учитывая это, мы можем превратить
ghci> map (\xs –> negate (sum (tail xs))) [[1..5],[3..6],[1..7]]
[–14,–15,–27]
в
ghci> map (negate . sum . tail) [[1..5],[3..6],[1..7]]
[–14,–15,–27]
Функция negate . sum . tail
принимает список, применяет к нему функцию tail
, суммирует результат и умножает полученное число на -1
. Получаем точный эквивалент анонимной функции из предыдущего примера.
Композиция функций с несколькими параметрами
Ну а как насчёт функций, которые принимают несколько параметров? Если мы хотим использовать их в композиции, обычно мы частично применяем их до тех пор, пока не получим функцию, принимающую только один параметр. Запись
sum (replicate 5 (max 6.7 8.9))
может быть преобразована так:
(sum . replicate 5) (max 6.7 8.9)
или так:
sum . replicate 5 $ max 6.7 8.9
Функция replicate 5
применяется к результату вычисления max 6.7 8.9
, после чего элементы полученного списка суммируются. Обратите внимание, что функция replicate
частично применена так, чтобы у неё остался только один параметр, так что теперь результат max 6.7 8.9
передаётся на вход replicate 5
; новым результатом оказывается список чисел, который потом передаётся функции sum
.
Если вы хотите переписать выражение с кучей скобок, используя функциональную композицию, можно сначала записать самую внутреннюю функцию с её параметрами, затем поставить перед ней знак $
, а после этого пристраивать вызовы всех других функций, записывая их без последнего параметра и разделяя точками. Например, выражение
replicate 2 (product (map (*3) (zipWith max [1,2] [4,5])))
можно переписать так:
replicate 2 . product . map (*3) $ zipWith max [1,2] [4,5]
Как из одного выражения получилось другое? Ну, во-первых, мы посмотрели на самую правую функцию и её параметры как раз перед группой закрывающихся скобок. Это функция zipWith max [1,2] [4,5]
. Так её и запишем:
zipWith max [1,2] [4,5]
Затем смотрим на функцию, которая применяется к zipWith max [1,2] [4,5]
, это map (*3)
. Поэтому мы ставим между ней и тем, что было раньше, знак $
:
map (*3) $ zipWith max [1,2] [4,5]
Теперь начинаются композиции. Проверяем, какая функция применяется ко всему этому, и присоединяем её к map (*3)
:
product . map (*3) $ zipWith max [1,2] [4,5]
Наконец, дописываем функцию replicate 2
и получаем окончательное выражение:
replicate 2 . product . map (*3) $ zipWith max [1,2] [4,5]
Если выражение заканчивалось на три закрывающие скобки, велики шансы, что у вас получится два оператора композиции.
Бесточечная нотация
Композиция функций часто используется и для так называемого бесточечного стиля записи функций. Возьмём, для примера, функцию, которую мы написали ранее:
sum' :: (Num a) => [a] –> a
sum' xs = foldl (+) 0 xs
Образец xs
представлен дважды с правой стороны. Из–за каррирования мы можем пропустить образец xs
с обеих сторон, так как foldl (+) 0
создаёт функцию, которая принимает на вход список. Если мы запишем эту функцию как sum' = foldl (+) 0
, такая запись будет называться бесточечной . А как записать следующее выражение в бесточечном стиле?
fn x = ceiling (negate (tan (cos (max 50 x))))
Мы не можем просто избавиться от образца x
с обеих правых сторон выражения. Образец x
в теле функции заключён в скобки. Выражение cos (max 50)
не будет иметь никакого смысла. Вы не можете взять косинус от функции! Всё, что мы можем сделать, – это выразить функцию fn
в виде композиции функций.
fn = ceiling . negate . tan . cos . max 50
Отлично! Во многих случаях бесточечная запись легче читается и более лаконична; она заставляет думать о функциях, о том, как их соединение порождает результат, а не о данных и способе их передачи. Можно взять простые функции и использовать композицию как «клей» для создания более сложных. Однако во многих случаях написание функций в бесточечном стиле может делать код менее «читабельным», особенно если функция слишком сложна. Вот почему я не рекомендую создавать длинные цепочки функций, хотя меня частенько обвиняли в пристрастии к композиции. Предпочитаемый стиль – использование выражения let
для присвоения меток промежуточным результатам или разбиение проблемы на подпроблемы и их совмещение таким образом, чтобы функции имели смысл для того, кто будет их читать, а не представляли собой огромную цепочку композиций.
Интервал:
Закладка: