关于折叠和堆栈溢出的问题

时间:2015-02-12 16:02:46

标签: haskell stack-overflow fold

了解一个Haskell 会谈到foldl'作为foldl的替代方法,因为foldl容易出现堆栈溢出。

  • 根据LYAH的说法,foldl (+) 0 (replicate 1000000 1)应该堆叠溢出,但它不在我的机器上。 为什么不呢?即使我将这个数字增加到1000万也不会溢出。它只占用大量内存,直到我的OS X计算机无法使用,我必须重新启动它。
  • 在什么情况下我应该使用foldl代替foldl'?根据我的经验foldl'“只是工作”,而foldl可能会导致我的计算机崩溃(见上文)。
  • 我不明白为什么foldr没有类似内容。为什么不能foldr堆栈溢出,为什么没有foldr'

2 个答案:

答案 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}}类型上,它可能有意义。