haskell-notes

по тому на каких типах они определены. Так например функция sort :: Ord a => [a] -> [a] определена

не в Prelude, а в отдельном библиотечном модуле для списков он называется Data.List. Так же есть много

других модулей для разных типов, таких как Data.Bool, Data.Char, Data.Function, Data.Maybe и многие

другие. Не пугайтесь изобилия модулей постепенно они станут вашей опорой.

Для поиска в стандартных библиотеках есть замечательный интернет-сервис Hoogle (http://www.

haskell.org/hoogle/). Hoogle может искать значения не только по имени, но и по типам. Например мы

хотим узнать целочисленный код символа. Поиск по типу Char -> Int выдаёт искомую функцию digitToInt.

2.8 Краткое содержание

В этой главе мы познакомились с интерпретатором ghci и основными типами. Рассмотрели много при-

меров.

Документация | 37

Типы

Bool

– Основные операции: &&, ||, not, if c then t else e

Char

– Значения пишутся в ординарных кавычках, как в ’H’, ’+’

String

– Значения пишутся в двойных кавычках, как в ”Hello World”

Int

– Эффективные целые числа, но ограниченные

Integer

– Не ограниченные целые числа, но не эффективные

Double

– Числа с двойной точностью

Float

– Числа с ординарной точностью

Rational

– Дробные числа

Нам впервые встретились кортежи (на функции properFraction). Кортежи используются для возвраще-

ния из функции нескольких значений. Элементы кортежа могут иметь разные типы. Для извлечения элемен-

тов из кортежей-пар используются функции fst и snd. Кортежи пишутся в скобках, и элементы разделены

запятыми:

(a, b)

(a, b, c)

(a, b, c, d)

Классы

Show

Печать

Eq

Сравнение на равенство

Num

Сложение и умножение

Fractional

Деление

Особенности синтаксиса

Запись применения функции:

Префиксная

Инфиксная

add a b

a ‘add‘ b

(+) a b

a + b

Также мы научились приводить одни численные типы к другим и пользоваться документацией.

2.9 Упражнения

• Напишите функцию beside :: Nat -> Nat -> Bool, которая будет возвращать True только в том случае,

если два аргумента находятся рядом, то есть один из них можно получить через другой операцией Succ.

• Напишите функцию beside2 :: Nat -> Nat -> Bool, которая будет возвращать True только если

аргументы являются соседями через некоторое другое число.

• Мы написали очень неэффективную функцию сложения натуральных чисел. Проблема в том, что число

рекурсивных вызовов функции зависит от величины второго аргумента. Если мы захотим прибавить

единицу к сотне, то порядок следования аргументов существенно повлияет на скорость вычисления.

Напишите функцию, которая лишена этого недостатка.

• Напишите функцию возведения в степень pow :: Nat -> Nat -> Nat.

• Напишите тип, описывающий бинарные деревья BinTree a. Бинарное дерево может быть либо листом

со значением типа a, либо хранить два поддерева.

• Напишите функцию reverse :: BinTree a -> BinTree a, которая переворачивает дерево. Она меняет

местами два элемента в узле дерева.

• Напишите функцию depth :: BinTree a -> Nat, которая вычисляет глубину дерева, то есть самый

длинный путь от корня дерева к листу.

38 | Глава 2: Первая программа

• Напишите функцию leaves :: BinTree a -> [a], которая переводит бинарное дерево в список, воз-

вращая все элементы в листьях дерева.

• Обратите внимание на раздел List Operations в Prelude. Посмотрите на функции и их типы. Попро-

буйте догадаться по типу функции и названию что она делает.

• Попробуйте разобраться по документации с классами Ord (сравнение на больше/меньше), Enum (пере-

числения) и Integral (целые числа). Также стоит отметить класс Floating. Если у вас не получится,

не беда, они обязательно встретятся нам вновь. Там и разберёмся.

• Найдите функцию, которая переставляет элементы пары местами (элементы могут быть разных типов).

Потренируйтесь с кортежами. Определите аналоги функций fst и snd для не пар. Обратите внимание

на то, что сочетание символов (,) это функция-конструктор пары:

Prelude> (,) ”Hi” 101

(”Hi”,101)

Prelude> :t (,)

(,) :: a -> b -> (a, b)

Также определены („), („,) и другие.

Упражнения | 39

Глава 3

Типы

С помощью типов мы определяем все возможные значения в нашей программе. Мы определяем основные

примитивы и способы их комбинирования. Например в типе Nat:

data Nat = Zero | Succ Nat

Один конструктор-примитив Zero, и один конструктор Succ, с помощью которого мы можем делать со-

ставные значения. Определив тип Nat таким образом, мы говорим, что значения типа Nat могут быть только

такими:

Zero,

Succ Zero,

Succ (Succ Zero), Succ (Succ (Succ Zero)),

Все значения являются цепочками Succ с Zero на конце. Если где-нибудь мы попытаемся построить значе-

ние, которое не соответствует нашему типу, мы получим ошибку компиляции, то есть программа не пройдёт

проверку типов. Так типы описывают множество допустимых значений.

Значения, которые проходят проверку типов мы будем называть допустимыми, а те, которые не проходят

соответственно недопустимыми. Так например следующие значения недопустимы для Nat

Succ Zero Zero,

Succ Succ, True, Zero (Zero Succ),

Недопустимых значений конечно гораздо больше. Такое проявляется и в естественном языке, бессмыс-

ленных комбинаций слов гораздо больше, чем осмысленных предложений. Обратите внимание на то, что мы

Страницы: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162