могли бы написать:
filter ::(a -> Bool) ->([a] ->[a])
filter =\p xs -> casexs of
...
но это лишь синтаксический сахар, который разворачивается в предыдущую запись.
Для тренировки определим несколько стандартных функций для работы с кортежами с помощью лямбда-
функций (все они определены в Prelude):
fst ::(a, b) ->a
fst =\(a, _) ->a
snd ::(a, b) ->b
snd =\( _, b) ->b
swap ::(a, b) ->(b, a)
swap =\(a, b) ->(b, a)
Обратите внимание на то, что все функции словно являются константами. Они не содержат аргументов.
Аргументы мы “пристраиваем” с помощью безымянных функций.
Определим функции преобразования первого и второго элемента кортежа (эти функции определены в
модуле Control.Arrow)
first ::(a ->a’) ->(a, b) ->(a’, b)
first =\f (a, b) ->(f a, b)
second ::(b ->b’) ->(a, b) ->(a, b’)
second =\f (a, b) ->(a, f b)
Также в Preludeесть полезные функции, которые превращают функции с частичным применением в
обычны функции и наоборот:
curry ::((a, b) ->c) ->a ->b ->c
curry =\f ->\a ->\b ->f (a, b)
uncurry ::(a ->b ->c) ->((a, b) ->c)
uncurry =\f ->\(a, b) ->f a b
66 | Глава 4: Декларативный и композиционный стиль
Функция curry принимает функцию двух аргументов для которой частичное применение невозможно.
Это имитируется с помощью кортежей. Функция принимает кортеж из двух элементов. Функция curry (от
слова каррирование, частичное применение) превращает такую функцию в обычную функцию Haskell. А
функция uncurry выполняет обратное преобразование.
С помощью лямбда-функций можно имитировать локальные переменные. Так например можно перепи-
сать формулу для вычисления площади треугольника:
square a b c =
(\p ->sqrt (p *(p -a) *(p -b) *(p -c)))
((a +b +c) /2)
Смотрите мы определили функцию, которая принимает параметром полупериметр p и передали в неё
значение ((a +b +c) /2). Если в нашей функции несколько локальных переменных, то мы можем
составить лямбда-функцию от нескольких переменных и подставить в неё нужные значения.
4.5 Какой стиль лучше?
Основной критерий выбора заключается в том, сделает ли этот элемент код более ясным . Наглядность
кода станет залогом успешной поддержки. Его будет легче понять и улучшить при необходимости.
Далее мы рассмотрим несколько примеров определений из Preludeи подумаем, почему был выбран тот
или иной стиль. Начнём с класса Ordи посмотрим на определения по умолчанию:
-- Тип упорядочивания
data
Ordering
=
LT | EQ | GT
deriving( Eq, Ord, Enum, Read, Show, Bounded)
class
( Eqa) => Orda
where
compare
::a ->a -> Ordering
( <), ( <=), ( >=), ( >) ::a ->a -> Bool
max, min
::a ->a ->a
-- Минимальное полное определение:
--
(<=) или compare
-- Использование compare может оказаться более
-- эффективным для сложных типов.
compare x y
|x ==y
=
EQ
|x <=y
=
LT
|otherwise =
GT
x <=y
=
compare x y /= GT
x <
y
=
compare x y == LT
x >=y
=
compare x y /= LT
x >
y
=
compare x y == GT
max x y
|x <=y
=
y
|otherwise =
x
min x y
|x <=y
=
x
|otherwise =
y
Все функции определены в декларативном стиле. Тип Orderingкодирует результат операции сравнения.
Два числа могут быть либо равны (значение EQ), либо первое меньше второго (значение LT), либо первое
больше второго (значение GT).
Обратите внимание на функцию compare. Мы не пишем дословное определение значений типа Ordering:
Читать дальше