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