Изучай Haskell во имя добра! (Липовача) - страница 80

Первое и последнее значения – минимальное и максимальное для >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. Вторая примечательная вещь – мы можем выполнять сопоставление с образцом по конструкторам. Мы уже записывали подобные сопоставления раньше (притом очень часто), когда сопоставляли со значениями