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

Единственное, что мы должны поменять, – это образцы. Мы игнорируем точку у образца для круга. В образце для прямоугольника используем вложенные образцы при сопоставлении для того, чтобы получить все поля точек. Если бы нам нужны были точки целиком, мы бы использовали именованные образцы. Проверим улучшенную версию:

>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

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

>baseRect :: Float –> Float –> Shape

>baseRect width height = Rectangle (Point 0 0) (Point width height)

Теперь создавать формы гораздо легче: достаточно создать форму в начале координат, а затем сдвинуть её в нужное место:

>ghci> nudge (baseRect 40 100) 60 23

>Rectangle (Point 60.0 23.0) (Point 100.0 123.0)

Фигуры на экспорт

Конечно же, вы можете экспортировать типы данных из модулей. Чтобы сделать это, запишите имена ваших типов вместе с именами экспортируемых функций. В отдельных скобках, через запятую, укажите, какие конструкторы значений вы хотели бы экспортировать. Если хотите экспортировать все конструкторы значений, просто напишите две точки >(..).

Если бы мы хотели поместить функции и типы, определённые выше, в модуль, то могли бы начать как-то так:

>module Shapes

>( Point(..)

>, Shape(..)

>, area

>, nudge

>, baseCircle

>, baseRect

>) where

Запись >Shape(..) обозначает, что мы экспортируем все конструкторы данных для типа >Shape. Тот, кто импортирует наш модуль, сможет создавать фигуры, используя конструкторы