在Haskell中汇总1到1,000,000会导致堆栈溢出。引擎盖下发生了什么?

时间:2014-01-29 00:00:00

标签: haskell sum stack-overflow

我有以下代码。

main = print $ sum [1..1000000]

当我跑步时,我得到一个堆栈溢出:

Stack space overflow: current size 8388608 bytes.
Use `+RTS -Ksize -RTS' to increase it.

我习惯于像Python这样的命令式语言,这些算法似乎没有问题:

sum(range(100000000))  # I'm not even using a generator.
4999999950000000

Haskell明显不同,但我不太明白导致堆栈溢出的原因是什么?在Haskell中导致堆栈溢出的问题是什么?

2 个答案:

答案 0 :(得分:10)

这整个问题仅与GHC< 7.10有关。在最近的版本中,sum [1..1000000]在常量空间中工作正常,至少在内置数字类型


sum isused to be implemented with the evil foldl1,它没有那么严格。因此,你从sum获得的东西本质上是一堆thunk,与你的输入一样大。我认为有一个讨论为什么它在某种程度上以这种方式完成... IMO它基本上只是愚蠢,因为总和通常不能懒得消耗,所以使用strict fold显然是显而易见的。

  

前奏> :m + Data.List
  Prelude Data.List> foldl'(+)0 [1..1000000]
  500000500000


1 实际上,foldl仅用于报告版本......但带累加器的显式递归版本当然不会更好。

答案 1 :(得分:3)

sum是以foldl的形式定义的,它以左关联方式延迟,因此在评估单个(在本例中为加法)表达式之前必须为整个列表生成thunk 。

您还可以使用sum更严格的对应foldl来定义foldl',如下所示:

sum' = foldl' (+) 0

有关foldl如何为每次计算生成thunks而不能评估任何内容的详细解释,请参阅Foldr. Foldl. Foldl'. from the Haskell Wiki。这将导致堆栈溢出。