Миран Липовача - Изучай Haskell во имя добра!
- Название:Изучай Haskell во имя добра!
- Автор:
- Жанр:
- Издательство:ДМК Пресс
- Год:2012
- Город:Москва
- ISBN:978-5-94074-749-9
- Рейтинг:
- Избранное:Добавить в избранное
-
Отзывы:
-
Ваша оценка:
Миран Липовача - Изучай Haskell во имя добра! краткое содержание
Язык Haskell имеет множество впечатляющих возможностей, но главное его свойство в том, что меняется не только способ написания кода, но и сам способ размышления о проблемах и возможных решениях. Этим Haskell действительно отличается от большинства языков программирования. С его помощью мир можно представить и описать нестандартным образом. И поскольку Haskell предлагает совершенно новые способы размышления о проблемах, изучение этого языка может изменить и стиль программирования на всех прочих.
Ещё одно необычное свойство Haskell состоит в том, что в этом языке придаётся особое значение рассуждениям о типах данных. Как следствие, вы помещаете больше внимания и меньше кода в ваши программы.
Вне зависимости от того, в каком направлении вы намерены двигаться, путешествуя в мире программирования, небольшой заход в страну Haskell себя оправдает. А если вы решите там остаться, то наверняка найдёте чем заняться и чему поучиться!
Эта книга поможет многим читателям найти свой путь к Haskell.
Отображения, монады, моноиды и другое! Всё сказано в названии: «Изучай Хаскель во имя добра!» – весёлый иллюстрированный самоучитель по этому сложному функциональному языку.
С помощью оригинальных рисунков автора, отсылке к поп-культуре, и, самое главное, благодаря полезным примерам кода, эта книга обучает основам функционального программирования так, как вы никогда не смогли бы себе представить.
Вы начнете изучение с простого материала: основы синтаксиса, рекурсия, типы и классы типов. Затем, когда вы преуспеете в основах, начнется настоящий мастер-класс от профессионала: вы изучите, как использовать аппликативные функторы, монады, застежки, и другие легендарные конструкции Хаскеля, о которых вы читали только в сказках.
Продираясь сквозь образные (и порой безумные) примеры автора, вы научитесь:
• Смеяться в лицо побочным эффектам, поскольку вы овладеете техниками чистого функционального программирования.
• Использовать волшебство «ленивости» Хаскеля для игры с бесконечными наборами данных.
• Организовывать свои программы, создавая собственные типы, классы типов и модули.
• Использовать элегантную систему ввода-вывода Хаскеля, чтобы делиться гениальностью ваших программ с окружающим миром.
Нет лучшего способа изучить этот мощный язык, чем чтение «Изучай Хаскель во имя добра!», кроме, разве что, поедания мозга его создателей. Миран Липовача (Miran Lipovača) изучает информатику в Любляне (Словения). Помимо его любви к Хаскелю, ему нравится заниматься боксом, играть на бас-гитаре и, конечно же, рисовать. У него есть увлечение танцующими скелетами и числом 71, а когда он проходит через автоматические двери, он притворяется, что на самом деле открывает их силой своей мысли.
Изучай Haskell во имя добра! - читать онлайн бесплатно ознакомительный отрывок
Интервал:
Закладка:
instance Monoid Ordering where
mempty = EQ
LT `mappend` _ = LT
EQ `mappend` y = y
GT `mappend` _ = GT
Экземпляр определяется следующим образом: когда мы объединяем два значения типа Ordering
с помощью функции mappend
, сохраняется значение слева, если значение слева не равно EQ
. Если значение слева равно EQ
, результатом будет значение справа. Единичным значением является EQ
. На первый взгляд, такой выбор может показаться несколько случайным, но на самом деле он имеет сходство с тем, как мы сравниваем слова в алфавитном порядке. Мы смотрим на первые две буквы, и, если они отличаются, уже можем решить, какое слово шло бы первым в словаре. Если же первые буквы равны, то мы переходим к сравнению следующей пары букв, повторяя процесс [13] Специалисты по нечёткой логике могут увидеть в этом определении троичную логику Лукасевича. – Прим. ред .
.

Например, сравнивая слова « ox » и « on », мы видим, что первые две буквы каждого слова равны, а затем продолжаем сравнивать вторые буквы. Поскольку « x » в алфавите идёт после « n », мы знаем, в каком порядке должны следовать эти слова. Чтобы лучше понять, как EQ
является единичным значением, обратите внимание, что если бы мы втиснули одну и ту же букву в одну и ту же позицию в обоих словах, их расположение друг относительно друга в алфавитном порядке осталось бы неизменным; к примеру, слово « oix » будет по-прежнему идти следом за « oin ».
Важно, что в экземпляре класса Monoid
для типа Ordering
выражение x `mappend` y
не равно выражению y `mappend` x
. Поскольку первый параметр сохраняется, если он не равен EQ
, LT `mappend` GT
в результате вернёт LT
, тогда как GT `mappend` LT
в результате вернёт GT
:
ghci> LT `mappend` GT
LT
ghci> GT `mappend` LT
GT
ghci> mempty `mappend` LT
LT
ghci> mempty `mappend` GT
GT
Хорошо, так чем же этот моноид полезен? Предположим, мы пишем функцию, которая принимает две строки, сравнивает их длину и возвращает значение типа Ordering
. Но если строки имеют одинаковую длину, то вместо того, чтобы сразу вернуть значение EQ
, мы хотим установить их расположение в алфавитном порядке.
Вот один из способов это записать:
lengthCompare :: String –> String –> Ordering
lengthCompare x y = let a = length x `compare` length y
b = x `compare` y
in if a == EQ then b else a
Результат сравнения длин мы присваиваем образцу a
, результат сравнения по алфавиту – образцу b
; затем, если оказывается, что длины равны, возвращаем их порядок по алфавиту.
Но, имея представление о том, что тип Ordering
является моноидом, мы можем переписать эту функцию в более простом виде:
import Data.Monoid
lengthCompare :: String –> String –> Ordering
lengthCompare x y = (length x `compare` length y) `mappend`(x `compare` y)
Давайте это опробуем:
ghci> lengthCompare "ямб" "хорей"
LT
ghci> lengthCompare "ямб" "хор"
GT
Вспомните, что когда мы используем функцию mappend
, сохраняется её левый параметр, если он не равен значению EQ
; если он равен EQ
, сохраняется правый. Вот почему мы поместили сравнение, которое мы считаем первым, более важным критерием, в качестве первого параметра. Теперь предположим, что мы хотим расширить эту функцию, чтобы она также сравнивала количество гласных звуков, и установить это вторым по важности критерием для сравнения. Мы изменяем её вот так:
import Data.Monoid
lengthCompare :: String –> String –> Ordering
lengthCompare x y = (length x `compare` length y) `mappend`
(vowels x `compare` vowels y) `mappend`
(x `compare` y)
where vowels = length . filter (`elem` "аеёиоуыэюя")
Мы создали вспомогательную функцию, которая принимает строку и сообщает нам, сколько она содержит гласных звуков, сначала отфильтровывая в ней только буквы, находящиеся в строке "аеёиоуыэюя"
, а затем применяя функцию length
.
ghci> lengthCompare "ямб" "абыр"
LT
ghci> lengthCompare "ямб" "абы"
LT
ghci> lengthCompare "ямб" "абр"
GT
В первом примере длины оказались различными, поэтому вернулось LT
, так как длина слова "ямб"
меньше длины слова "абыр"
. Во втором примере длины равны, но вторая строка содержит больше гласных звуков, поэтому опять возвращается LT
. В третьем примере они обе имеют одинаковую длину и одинаковое количество гласных звуков, поэтому сравниваются по алфавиту, и слово "ямб"
выигрывает.
Моноид для типа Ordering
очень полезен, поскольку позволяет нам без труда сравнивать сущности по большому количеству разных критериев и помещать сами эти критерии по порядку, начиная с наиболее важных и заканчивая наименее важными.
Моноид Maybe
Рассмотрим несколько способов, которыми для типа Maybe a
могут быть определены экземпляры класса Monoid
, и обсудим, чем эти экземпляры полезны.
Один из способов состоит в том, чтобы обрабатывать тип Maybe a
как моноид, только если его параметр типа a
тоже является моноидом, а потом реализовать функцию mappend
так, чтобы она использовала операцию mappend
для значений, обёрнутых в конструктор Just
. Мы используем значение Nothing
как единичное, и поэтому если одно из двух значений, которые мы объединяем с помощью функции mappend
, равно Nothing
, мы оставляем другое значение. Вот объявление экземпляра:
instance Monoid a => Monoid (Maybe a) where
mempty = Nothing
Nothing `mappend` m = m
m `mappend` Nothing = m
Just m1 `mappend` Just m2 = Just (m1 `mappend` m2)
Обратите внимание на ограничение класса. Оно говорит, что тип Maybe
является моноидом, только если для типа a
определён экземпляр класса Monoid
. Если мы объединяем нечто со значением Nothing
, используя функцию mappend
, результатом является это нечто. Если мы объединяем два значения Just
с помощью функции mappend
, то содержимое значений Just
объединяется с помощью этой функции, а затем оборачивается обратно в конструктор Just
. Мы можем делать это, поскольку ограничение класса гарантирует, что тип значения, которое находится внутри Just
, имеет экземпляр класса Monoid
.
ghci> Nothing `mappend` Just "андрей"
Just "андрей"
ghci> Just LT `mappend` Nothing
Just LT
ghci> Just (Sum 3) `mappend` Just (Sum 4)
Just (Sum {getSum = 7})
Это полезно, когда мы имеем дело с моноидами как с результатами вычислений, которые могли окончиться неуспешно. Из-за наличия этого экземпляра нам не нужно проверять, окончились ли вычисления неуспешно, определяя, вернули они значение Nothing
или Just
; мы можем просто продолжить обрабатывать их как обычные моноиды.
Интервал:
Закладка: