Функции высшего порядка, такие как foldr
, foldl
, map
, zipWith
и т. Д.захватывать общие шаблоны рекурсии, чтобы избежать написания рекурсивных определений вручную.Это делает ваш код более высокоуровневым и более читабельным: вместо того, чтобы переходить по коду и делать выводы о том, что делает рекурсивная функция, программист может рассуждать о композициях компонентов более высокого уровня.
Для несколько экстремальныхНапример, рассмотрим рекурсивный расчет стандартного отклонения вручную:
standardDeviation numbers = step1 numbers
where
-- Calculate length and sum to obtain mean
step1 = loop 0 0
where
loop count sum (x : xs) = loop (count + 1) (sum + x) xs
loop count sum [] = step2 sum count numbers
-- Calculate squared differences with mean
step2 sum count = loop []
where
loop diffs (x : xs) = loop ((x - (sum / count)) ^ 2 : diffs) xs
loop diffs [] = step3 count diffs
-- Calculate final total and return square root
step3 count = loop 0
where
loop total (x : xs) = loop (total + x) xs
loop total [] = sqrt (total / count)
(Чтобы быть справедливым, я немного переборщил, также указав суммирование, но это примерно так, как это обычно делается в императивном языке).- зацикливание вручную.)
Теперь рассмотрим версию, использующую композицию вызовов стандартных функций, некоторые из которых более высокого порядка:
standardDeviation numbers -- The standard deviation
= sqrt -- is the square root
. mean -- of the mean
. map (^ 2) -- of the squares
. map (subtract -- of the differences
(mean numbers)) -- with the mean
$ numbers -- of the input numbers
where -- where
mean xs -- the mean
= sum xs -- is the sum
/ fromIntegral (length xs) -- over the length.
Этот более декларативный код также, я надеюсь,гораздо более читабельный - и без тяжелых комментариев, может быть написан аккуратно в две строки.Это также намного более очевидно, чем рекурсивная версия низкого уровня.
Более того, sum
, map
и length
могут быть реализованы в терминах сгибов, а также многих других стандартных функций.как product
, and
, or
, concat
и т. д.Складывание является чрезвычайно распространенной операцией не только для списков, но и для всех типов контейнеров (см. Класс типов Foldable
), поскольку оно захватывает шаблон вычисления чего-то постепенно для всех элементов контейнера.
Последняя причинаиспользование сгибов вместо ручной рекурсии является производительностью: благодаря лени и оптимизациям, которые GHC знает, как выполнять при использовании функций на основе fold
, компилятор может объединить серию сгибов (отображений и т. д.) в один циклво время выполнения.