页面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'
仍然会建立未评估的 thunk。
显而易见的解决方案是更改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在下面的评论中建议的那样)