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

Вот реализация функции >filter:

>filter' :: (a -> Bool) -> [a] -> [a]

>filter' p = foldr (\x acc -> if p x then x : acc else acc) []

Здесь начальное значение аккумулятора является пустым списком. Мы сворачиваем список справа налево и проверяем каждый элемент, пользуясь предикатом >p. Если >p x возвращает истину, элемент >x помещается в начало аккумулятора. В противном случае аккумулятор остаётся без изменения.

Напоследок реализуем функцию >last:

>last' :: [a] -> a

>last' = foldl1 (\ x -> x)

Для получения последнего элемента списка мы применяем >foldr1. Начинаем с «головы» списка, а затем применяем бинарную функцию, которая игнорирует аккумулятор и устанавливает текущий элемент списка как новое значение аккумулятора. Как только мы достигаем конца списка, аккумулятор — то есть последний элемент – возвращается в качестве результата свёртки.

Иной взгляд на свёртки

Есть ещё один способ представить работу правой и левой свёртки. Скажем, мы выполняем правую свёртку с бинарной функцией >f и стартовым значением >z. Если мы применяем правую свёртку к списку >[3,4,5,6], то на самом деле вычисляем вот что:

>f 3 (f 4 (f 5 (f 6 z)))

Функция >f вызывается с последним элементом в списке и аккумулятором; получившееся значение передаётся в качестве аккумулятора при вызове функции с предыдущим значением, и т. д. Если мы примем функцию >f за операцию сложения и начальное значение за нуль, наш пример преобразуется так:

>3 + (4 + (5 + (6 + 0)))

Или, если записать оператор >+ как префиксную функцию, получится:

>(+) 3 ((+) 4 ((+) 5 ((+) 6 0)))

Аналогичным образом левая свёртка с бинарной функцией >g и аккумулятором >z является эквивалентом выражения

>g (g (g (g z 3) 4) 5) 6

Если заменить бинарную функцию на >flip (:) и использовать >[] как аккумулятор (выполняем обращение списка), подобная запись эквивалентна следующей:

>flip (:) (flip (:) (flip (:) (flip (:) [] 3) 4) 5) 6

Если вычислить это выражение, мы получим >[6,5,4,3].

Свёртка бесконечных списков

Взгляд на свёртки как на последовательное применение функции к элементам списка помогает понять, почему правая свёртка иногда отлично работает с бесконечными списками. Давайте реализуем функцию >and с помощью >foldr, а потом выпишем последовательность применений, как мы это делали в предыдущих примерах. Тогда мы увидим, как ленивость языка Haskell позволяет правой свёртке обрабатывать бесконечные списки.

Функция >and принимает список значений типа >Bool и возвращает >False, если хотя бы один из элементов равен >False; в противном случае она возвращает >True. Мы будем обходить список справа, используя