haskell-notes

этом определении мы видим новое ключевое слово type. До этого для определения типов нам встречалось

лишь слово data. Ключевое слово type определяет синоним типа. При этом мы не вводим новый тип, мы

лишь определяем для него псевдоним. String является синонимом для списка значений типа Char. Тип

Char представляет символы. Итак строка – это список символов. В Haskell символы пишутся в ординарных

кавычках, а строки в двойных:

Prelude> [’H’,’e’,’l’,’l’,’o’]

”Hello”

it :: [Char]

Prelude> ”Hello”

”Hello”

it :: [Char]

Prelude> ’+’

’+’

it :: Char

Для обозначения перехода на новую строку используется специальный символ n. Если строка слишком

длинная и не помещается на одной строке, то её можно перенести так:

str = ”My long long long long

long long string”

Перенос осуществляется с помощью комбинации следующих друг за другом обратных слэшей.

Нам понадобится функция конкатенации списков (++), она определена в Prelude, с её помощью мы будем

объединять строки:

Prelude> :t (++)

(++) :: [a] -> [a] -> [a]

Prelude> ”Hello” ++ [’ ’] ++ ”World”

”Hello World”

it :: [Char]

Пример: Отображение дат и времени

Приведём, пример в котором отображаемое значение не совпадает с видом значения в коде. Мы отобра-

зим значения из мира календаря. Для начала давайте сохраним определения в отдельном модуле:

module Calendar where

import Prelude (Int, Char, String, Show(.. ), (++))

— Дата

Класс Show. Строки и символы | 29

data Date = Date Year Month Day

— Год

data Year

= Year Int

— Int это целые числа

— Месяц

data Month

= January

| February

| March

| April

| May

| June

| July

| August

| September

| October

| November | December

data Day = Day Int

— Неделя

data Week

= Monday

| Tuesday

| Wednesday

| Thursday

| Friday

| Saturday

| Sunday

— Время

data Time = Time Hour Minute Second

data Hour

= Hour

Int

— Час

data Minute = Minute Int

— Минута

data Second = Second Int

— Секунда

Теперь сохраним наш модуль под именем Calendar. hs и загрузим в интерпретатор:

Prelude> :l Calendar

[1 of 1] Compiling Calendar

( Calendar. hs, interpreted )

Ok, modules loaded: Calendar.

*Calendar> Monday

< interactive>:3:1:

No instance for (Show Week)

arising from a use of System.IO. print’

Possible fix: add an instance declaration for (Show Week)

In a stmt of an interactive GHCi command: System.IO. print it

Смотрите мы попытались распечатать значение Monday, но в ответ получили ошибку. В ней интерпре-

татор сообщает нам о том, что для типа Week не определён экземпляр класса Show, и он не знает как его

распечатывать. Давайте подскажем ему. Обычно дни недели в календарях печатают не полностью, в имя

попадают лишь три первых буквы:

instance Show Week where

show Monday

= ”Mon”

show Tuesday

= ”Tue”

show Wednesday

= ”Wed”

show Thursday

= ”Thu”

show Friday

= ”Fri”

show Saturday

= ”Sat”

show Sunday

= ”Sun”

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

так писать. По отступам компилятор понимает, что все определения относятся к определению instance.

Теперь запишем экземпляр в модуль, сохраним, и перезагрузим в интерпретатор:

*Calendar> :r

[1 of 1] Compiling Calendar

( Calendar. hs, interpreted )

Ok, modules loaded: Calendar.

*Calendar> Monday

Mon

it :: Week

*Calendar> Sunday

Sun

it :: Week

Теперь наши дни отображаются. Я выпишу ещё один пример экземпляра для Time, а остальные достанутся

вам в качестве упражнения.

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

instance Show Time where

show (Time h m s) = show h ++ ”:” ++ show m ++ ”:” ++ show s

instance Show Hour where

show (Hour h) = addZero (show h)

instance Show Minute where

show (Minute m) = addZero (show m)

instance Show Second where

show (Second s) = addZero (show s)

addZero :: String -> String

addZero (a:[]) = ’0’ : a : []

addZero as

= as

Функцией addZero мы добавляем ноль в начало строки, в том случае, если число однозначное, также в

этом определении мы воспользовались тем, что для типа целых чисел Int экземпляр Show уже определён.

Проверим в интерпретаторе:

*Calendar> Time (Hour 13) (Minute 25) (Second 2)

13:25:02

it :: Time

2.5 Автоматический вывод экземпляров классов типов

Для некоторых стандартных классов экземпляры классов типов могут быть выведены автоматически.

Это делается с помощью директивы deriving. Она пишется сразу после объявления типа. Например так мы

можем определить тип и экземпляры для классов Show и Eq:

data T = A | B | C

deriving (Show, Eq)

Отступ за deriving обязателен, после ключевого слова в скобках указываются классы, которые мы хотим

вывести.

2.6 Арифметика

В этом разделе мы обсудим основные арифметические операции. В Haskell много стандартных классов,

которые группируют различные типы операций, есть класс для сравнения на равенство, отдельный класс для

сравнения на больше/меньше, класс для умножения, класс для деления, класс для упорядоченных чисел, и

много других. Зачем такое изобилие классов?

Каждый из классов отвечает независимой группе операций. Есть много объектов, которые можно только

складывать, но нельзя умножать или делить. Есть объекты, для которых сравнение на равенство имеет смысл,

а сравнение на больше/меньше – нет.

Для иллюстрации мы воспользуемся числами Пеано, у них компактное определение, всего два конструк-

Страницы: 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