Миран Липовача - Изучай Haskell во имя добра!
- Название:Изучай Haskell во имя добра!
- Автор:
- Жанр:
- Издательство:ДМК Пресс
- Год:2012
- Город:Москва
- ISBN:978-5-94074-749-9
- Рейтинг:
- Избранное:Добавить в избранное
-
Отзывы:
-
Ваша оценка:
Миран Липовача - Изучай Haskell во имя добра! краткое содержание
Язык Haskell имеет множество впечатляющих возможностей, но главное его свойство в том, что меняется не только способ написания кода, но и сам способ размышления о проблемах и возможных решениях. Этим Haskell действительно отличается от большинства языков программирования. С его помощью мир можно представить и описать нестандартным образом. И поскольку Haskell предлагает совершенно новые способы размышления о проблемах, изучение этого языка может изменить и стиль программирования на всех прочих.
Ещё одно необычное свойство Haskell состоит в том, что в этом языке придаётся особое значение рассуждениям о типах данных. Как следствие, вы помещаете больше внимания и меньше кода в ваши программы.
Вне зависимости от того, в каком направлении вы намерены двигаться, путешествуя в мире программирования, небольшой заход в страну Haskell себя оправдает. А если вы решите там остаться, то наверняка найдёте чем заняться и чему поучиться!
Эта книга поможет многим читателям найти свой путь к Haskell.
Отображения, монады, моноиды и другое! Всё сказано в названии: «Изучай Хаскель во имя добра!» – весёлый иллюстрированный самоучитель по этому сложному функциональному языку.
С помощью оригинальных рисунков автора, отсылке к поп-культуре, и, самое главное, благодаря полезным примерам кода, эта книга обучает основам функционального программирования так, как вы никогда не смогли бы себе представить.
Вы начнете изучение с простого материала: основы синтаксиса, рекурсия, типы и классы типов. Затем, когда вы преуспеете в основах, начнется настоящий мастер-класс от профессионала: вы изучите, как использовать аппликативные функторы, монады, застежки, и другие легендарные конструкции Хаскеля, о которых вы читали только в сказках.
Продираясь сквозь образные (и порой безумные) примеры автора, вы научитесь:
• Смеяться в лицо побочным эффектам, поскольку вы овладеете техниками чистого функционального программирования.
• Использовать волшебство «ленивости» Хаскеля для игры с бесконечными наборами данных.
• Организовывать свои программы, создавая собственные типы, классы типов и модули.
• Использовать элегантную систему ввода-вывода Хаскеля, чтобы делиться гениальностью ваших программ с окружающим миром.
Нет лучшего способа изучить этот мощный язык, чем чтение «Изучай Хаскель во имя добра!», кроме, разве что, поедания мозга его создателей. Миран Липовача (Miran Lipovača) изучает информатику в Любляне (Словения). Помимо его любви к Хаскелю, ему нравится заниматься боксом, играть на бас-гитаре и, конечно же, рисовать. У него есть увлечение танцующими скелетами и числом 71, а когда он проходит через автоматические двери, он притворяется, что на самом деле открывает их силой своей мысли.
Изучай Haskell во имя добра! - читать онлайн бесплатно ознакомительный отрывок
Интервал:
Закладка:
data Int = –2147483648 | –2147483647 | ... | –1 | 0 | 1 | 2 | ... | 2147483647
Первое и последнее значения – минимальное и максимальное для Int
. На самом деле тип Int
объявлен иначе – видите, я пропустил уйму чисел – такая запись полезна лишь в иллюстративных целях.
Отличная фигура за 15 минут
Теперь подумаем, как бы мы представили некую геометрическую фигуру в языке Haskell. Один из способов – использовать кортежи. Круг может быть представлен как (43.1, 55.0, 10.4)
, где первое и второе поле – координаты центра, а третье – радиус. Вроде бы подходит, но такой же кортеж может представлять вектор в трёхмерном пространстве или что-нибудь ещё. Лучше было бы определить свой собственный тип для фигуры. Скажем, наша фигура может быть кругом или прямоугольником.
data Shape = Circle Float Float Float | Rectangle Float Float Float Float
Ну и что это? Размышляйте следующим образом. Конструктор для значения Circle
содержит три поля типа Float
. Когда мы записываем конструктор значения типа, опционально мы можем добавлять типы после имени конструктора; эти типы определяют, какие значения будет содержать тип с данным конструктором. В нашем случае первые два числа – это координаты центра, третье число – радиус. Конструктор для значения Rectangle
имеет четыре поля, которые также являются числами с плавающей точкой. Первые два числа – это координаты верхнего левого угла, вторые два числа – координаты нижнего правого угла.
Когда я говорю «поля», то подразумеваю «параметры». Конструкторы данных на самом деле являются функциями, только эти функции возвращают значения типа данных. Давайте посмотрим на сигнатуры для наших двух конструкторов:
ghci> :t Circle
Circle :: Float –> Float –> Float –> Shape
ghci> :t Rectangle
Rectangle :: Float –> Float –> Float –> Float –> Shape
Классно, конструкторы значений – такие же функции, как любые другие! Кто бы мог подумать!..
Давайте напишем функцию, которая принимает фигуру и возвращает площадь её поверхности:
area :: Shape –> Float
area (Circle _ _ r) = pi * r ^ 2
area (Rectangle x1 y1 x2 y2) = (abs $ x2 – x1) * (abs $ y2 – y1)
Первая примечательная вещь в объявлении – это декларация типа. Она говорит, что функция принимает фигуру и возвращает значение типа Float
. Мы не смогли бы записать функцию типа Circle –> Float
, потому что идентификатор Circle
не является типом; типом является идентификатор Shape
. По той же самой причине мы не смогли бы написать функцию с типом True –> Int
. Вторая примечательная вещь – мы можем выполнять сопоставление с образцом по конструкторам. Мы уже записывали подобные сопоставления раньше (притом очень часто), когда сопоставляли со значениями []
, False
, 5
, только эти значения не имели полей. Только что мы записали конструктор и связали его поля с именами. Так как для вычисления площади нам нужен только радиус, мы не заботимся о двух первых полях, которые говорят нам, где располагается круг.
ghci> area $ Circle 10 20 10
314.15927
ghci> area $ Rectangle 0 0 100 100
10000.0
Ура, работает! Но если попытаться напечатать Circle 10 20 5
в командной строке интерпретатора, то мы получим ошибку. Пока Haskell не знает, как отобразить наш тип данных в виде строки. Вспомним, что когда мы пытаемся напечатать значение в командной строке, интерпретатор языка Haskell вызывает функцию show
, для того чтобы получить строковое представление значения, и затем печатает результат в терминале. Чтобы определить для нашего типа Shape
экземпляр класса Show
, модифицируем его таким образом:
data Shape = Circle Float Float Float | Rectangle Float Float Float Float
deriving (Show)
Не будем пока концентрировать внимание на конструкции deriving (Show)
. Просто скажем, что если мы добавим её в конец объявления типа данных, Haskell автоматически определит экземпляр класса Show
для этого типа. Теперь можно делать так:
ghci> Circle 10 20 5
Circle 10.0 20.0 5.0
ghci> Rectangle 50 230 60 90
Rectangle 50.0 230.0 60.0 90.0
Конструкторы значений – это функции, а значит, мы можем их отображать, частично применять и т. д. Если нам нужен список концентрических кругов с различными радиусами, напишем следующий код:
ghci> map (Circle 10 20) [4,5,6,6]
[Circle 10.0 20.0 4.0,Circle 10.0 20.0 5.0,Circle 10.0 20.0 6.0,Circle 10.0 20.0 6.0]
Верный способ улучшить фигуру
Наш тип данных хорош, но может быть и ещё лучше. Давайте создадим вспомогательный тип данных, который определяет точку в двумерном пространстве. Затем используем его для того, чтобы сделать наши фигуры более понятными:
data Point = Point Float Float deriving (Show)
data Shape = Circle Point Float | Rectangle Point Point deriving (Show)
Обратите внимание, что при определении точки мы использовали одинаковые имена для конструктора типа и для конструктора данных. В этом нет какого-то особого смысла, но если у типа данных только один конструктор, как правило, он носит то же имя, что и тип. Итак, теперь у конструктора Circle
два поля: первое имеет тип Point
, второе – Float
. Так легче разобраться, что есть что. То же верно и для прямоугольника. Теперь, после всех изменений, мы должны исправить функцию area
:
area :: Shape –> Float
area (Circle _ r) = pi * r 2
area (Rectangle (Point x1 y1) (Point x2 y2)) = (abs $ x2 – x1) * (abs $ y2 – y1)
Единственное, что мы должны поменять, – это образцы. Мы игнорируем точку у образца для круга. В образце для прямоугольника используем вложенные образцы при сопоставлении для того, чтобы получить все поля точек. Если бы нам нужны были точки целиком, мы бы использовали именованные образцы. Проверим улучшенную версию:
ghci> area (Rectangle (Point 0 0) (Point 100 100))
10000.0
ghci> area (Circle (Point 0 0) 24)
1809.5574
Как насчёт функции, которая двигает фигуру? Она принимает фигуру, приращение координаты по оси абсцисс, приращение координаты по оси ординат – и возвращает новую фигуру, которая имеет те же размеры, но располагается в другом месте.
nudge :: Shape –> Float –> Float –> Shape
nudge (Circle (Point x y) r) a b = Circle (Point (x+a) (y+b)) r
nudge (Rectangle (Point x1 y1) (Point x2 y2)) a b
= Rectangle (Point (x1+a) (y1+b)) (Point (x2+a) (y2+b))
Всё довольно очевидно. Мы добавляем смещение к точкам, определяющим положение фигуры:
ghci> nudge (Circle (Point 34 34) 10) 5 10
Circle (Point 39.0 44.0) 10.0
Если мы не хотим иметь дело напрямую с точками, то можем сделать вспомогательные функции, которые создают фигуры некоторого размера с нулевыми координатами, а затем их подвигать.
Во-первых, напишем функцию, принимающую радиус и создающую круг с указанным радиусом, расположенный в начале координат:
baseCircle :: Float –> Shape
baseCircle r = Circle (Point 0 0) r
Добавим функцию, которая по заданным ширине и высоте создаёт прямоугольник соответствующего размера. При этом левый нижний угол прямоугольника находится в начале координат:
Читать дальшеИнтервал:
Закладка: