Учебник по Haskell | страница 49



придумайте какие-нибудь новые.

Строчная запись деревьев

Итак все константы в Haskell за счёт особой структуры построения типов являются деревьями, но мы

программируем в текстовом редакторе, а не в редакторе векторной графики, поэтому нам нужен удобный

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

Мы сидим на корне дерева и спускаемся по его вершинам. Нам могут встретиться вершины двух типов

узлы и листья. Сначала мы пишем имя в текущем узле, затем через пробел имена в дочерних узлах, если нам

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

записи дерево из первого примера:

Начнём с корня и будем последовательно дописывать поддеревья, точками обозначаются дочерние узлы,

которые нам ещё предстоит дописать:

(1

.

.

.

)

(1

(3 . )

5

(6 . . . ))

(1

(3 4)

5

(6 2 7 8))

44 | Глава 3: Типы

1

3

5

6

4

2

7

8

Рис. 3.6: Ориентированное дерево

Мы можем ставить любое число пробелов между дочерними узлами, здесь для наглядности точки вы-

ровнены. Так мы можем закодировать исходное дерево строкой. Часто самые внешние скобки опускаются. В

итоге получилась такая запись:

tree = 1 (3 4) 5 (6 2 7 8)

По этой записи мы можем понять, что у нас есть два конструктора трёх аргументов 1 и 6, один конструктор

одного аргумента 3 и пять примитивных конструкторов. Точно так же мы строим и все другие константы в

Haskell:

Succ (Succ (Succ Zero))

Time (Hour 13) (Minute 10) (Second 0)

Mul (Add One Ten) (Neg (Mul Six Zero))

За одним исключением, если конструктор бинарный, символьный (начинается с двоеточия), мы помеща-

ем его между аргументов:

(One :+ Ten) :* (Neg (Six :* Zero))

3.3 Структура функций

Функции описывают одни значения в терминах других. При этом важно понимать, что функция это лишь

новое имя, пусть и составное. Мы можем написать 5, или 2+3, это лишь два разных имени для одной кон-

станты. Теперь мы разобрались с тем, что константы это деревья. Значит функции строят одни деревья из

других. Как они это делают? Для этого этого в Haskell есть две операции: это композиция и декомпозиция де-

ревьев. С помощью композиции мы строим из простых деревьев сложные, а с помощью декомпозиции разбиваем

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

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

определения синонима. Давайте вспомним какое-нибудь объявление функции:

(+) a

Zero

= a

(+) a

(Succ b)

= Succ (a + b)

Смотрите в этой функции слева от знака равно мы проводим декомпозицию второго аргумента, а в правой