Страница Foldr Foldl Foldl'обсуждает foldl'
и определяет его следующим образом:
foldl' f z [] = z
foldl' f z (x:xs) = let z' = z `f` x
in seq z' $ foldl' f z' xs
Это делается для того, чтобы избежать утечек пространства, т. е. поэтому fold'
, который дает результат постоянного размера, использует только постоянное пространство.
Однако это не обязательно работает, как указано здесь:
Задействованная функция
seq
вычисляет только самый верхний конструктор. Если аккумулятор является более сложным объектом, тоfold'
по-прежнему будет создавать неоцененные санки.
Очевидное решение — заменить seq
наdeepseq
, как показано (при условии, что вы работаете сNFData
):
foldl_strict f z [] = z
foldl_strict f z (x:xs) = let z' = z `f` x
in deepseq z' $ foldl_strict f z' xs
Но у меня такое чувство это может быть ужасно неэффективно, так как вся структура должна будет проходить deepseq
при каждом проходе цикла (если только компилятор не сможет статически доказать, что в этом нет необходимости).
Затем я попробовал следующее:
foldl_stricter f z l = deepseq z $ foldl_stricter' f z l
foldl_stricter' f z [] = z
foldl_stricter' f z (x:xs) = let z' = deepseq x $ z `f` x
in seq z' $ foldl_stricter' f z' xs
Но обнаружил, что у него есть проблема. Приведенный ниже код терпит неудачу, когда должен возвращать 3.
foldl_stricter (\x y -> x + head y) 0 [[1..],[2..]]
Таким образом, fold_stricter
слишком строг. Список не обязательно должен быть строгим, для предотвращения утечки пространства важно, чтобы аккумулятор был строгим. fold_stricter
заходит слишком далеко и также делает список строгим, что приводит к сбою вышеописанного.
Что возвращает нас к fold_strict
.Повторный запуск deepseq
для структуры данных размером n
занимает O(n)
времени или только O(n)
времени в первый раз и O(1)
после этого? (Как предлагает dbauppв своем комментарииниже)