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

как начальное значение. В качестве бинарной функции будем использовать операцию >&&, потому что должны вернуть >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..]. Теперь, чтобы получить сумму, прибегнем к помощи свёртки, но поскольку нам интересно знать, как увеличивается сумма, будем вызывать функцию