как начальное значение. В качестве бинарной функции будем использовать операцию
>&&
, потому что должны вернуть
>True
только в том случае, когда все элементы списка истинны. Функция
>&&
возвращает
>False
, если хотя бы один из параметров равен
>False
, поэтому если мы встретим в списке
>False
, то аккумулятор будет установлен в значение
>False
и окончательный результат также будет
>False
, даже если среди оставшихся элементов списка обнаружатся истинные значения.
>and' :: [Bool] -> Bool
>and' xs = foldr (&&) True xs
Зная, как работает >foldr
, мы видим, что выражение >and' [True,False,True]
будет вычисляться следующим образом:
>True && (False && (True && True))
Последнее >True
здесь – это начальное значение аккумулятора, тогда как первые три логических значения взяты из списка >[True,False,True]
. Если мы попробуем вычислить результат этого выражения, получится >False
.
А что если попробовать то же самое с бесконечным списком, скажем, >repeat False
? Если мы выпишем соответствующие применения, то получится вот что:
>False && (False && (False && (False …
Ленивость Haskell позволит вычислить только то, что действительно необходимо. Функция >&&
устроена таким образом, что если её первый параметр >False
, то второй просто игнорируется, поскольку и так ясно, что результат должен быть >False
.
Функция >foldr
будет работать с бесконечными списками, если бинарная функция, которую мы ей передаём, не требует обязательного вычисления второго параметра, если значения первого ей достаточно для вычисления результата. Такова функция >&&
– ей неважно, каков второй параметр, при условии, что первый — >False
.
Функции >scanl
и >scanr
похожи на >foldl
и >foldr
, только они сохраняют все промежуточные значения аккумулятора в список. Также существуют функции >scanl1
и >scanr1
, которые являются аналогами >foldl1
и >foldr1
.
>ghci> scanl (+) 0 [3,5,2,1]
>[0,3,8,10,11]
>ghci> scanr (+) 0 [3,5,2,1]
>[11,8,3,1,0]
>ghci> scanl1 (\acc x –> if x > acc then x else acc) [3,4,5,3,7,9,2,1]
>[3,4,5,5,7,9,9,9]
>ghci> scanl (flip (:)) [] [3,2,1]
>[[],[3],[2,3],[1,2,3]]
При использовании функции >scanl
финальный результат окажется в последнем элементе итогового списка, тогда как функция >scanr
поместит результат в первый элемент.
Функции сканирования используются для того, чтобы увидеть, как работают функции, которые можно реализовать как свёртки. Давайте ответим на вопрос: как много корней натуральных чисел нам потребуется, чтобы их сумма превысила 1000? Чтобы получить сумму квадратов натуральных чисел, воспользуемся >map sqrt [1..]
. Теперь, чтобы получить сумму, прибегнем к помощи свёртки, но поскольку нам интересно знать, как увеличивается сумма, будем вызывать функцию