了解一个Haskell 会谈到foldl'
作为foldl
的替代方法,因为foldl
容易出现堆栈溢出。
foldl (+) 0 (replicate 1000000 1)
应该堆叠溢出,但它不在我的机器上。 为什么不呢?即使我将这个数字增加到1000万也不会溢出。它只占用大量内存,直到我的OS X计算机无法使用,我必须重新启动它。foldl
代替foldl'
?根据我的经验foldl'
“只是工作”,而foldl
可能会导致我的计算机崩溃(见上文)。foldr
没有类似内容。为什么不能foldr
堆栈溢出,为什么没有foldr'
?答案 0 :(得分:12)
它不会因堆栈溢出而崩溃,因为默认情况下堆栈现在是无限的。也就是说,默认的GHC运行时行为是允许堆栈无限增长 - 没有绑定可以触发堆栈溢出错误。
https://ghc.haskell.org/trac/ghc/ticket/8189
以下是对其工作原理的描述:
线程堆栈(包括主线程的堆栈)存在于堆上。如 堆栈增长,根据需要添加新的堆栈块;如果堆栈 再次收缩,这些额外的堆栈块被垃圾回收 集电极。默认的初始堆栈大小是故意小的 为了保持线程创建的时间和空间开销 最小化,并使生成线程的实用性甚至很小 工作。
答案 1 :(得分:4)
为什么不能折叠堆叠溢出,为什么没有折叠'?
嗯,foldr
不是尾递归,即它不直接调用自己:
foldr f a (x:xs) = f x (foldr f a xs)
减少foldr
后,控件将传递给用户提供的f
。因此,在将参数传递给foldr'
之前,不需要强制参数f
:如果需要,调用者可以通过严格f
(例如利用爆炸模式或seq
)。
堆栈是否溢出取决于f
的作用。例如,
f x y = x
将导致仅在第一个元素中访问列表。相反,
f x y = seq y x
可能导致堆栈溢出(或内存密集型行为)。取而代之的是,
f x y = x+1 : y
会导致输出列表懒散地生成,类似于map (+1) xs
,没有任何令人讨厌的意外。
正如@dfeuer所指出的那样,Data.Foldable.foldr'
存在Foldable
作为严格的右侧折叠对任何Foldable
进行操作。在列表上,这几乎是多余的,如上所述。在其他{{1}}类型上,它可能有意义。