Haskell - 垃圾收集无法回收足够的空间

时间:2013-02-07 11:27:28

标签: haskell stack stack-overflow

我正在执行一项程序,将所有奇数加到n:

oddSum' n result | n==0 = result
                 | otherwise = oddSum' (n-1) ((mod n 2)*(n)+result)

oddSum n = oddSum' n 0

我为我的输入获得了两个错误(我将它们放在下面),我使用尾递归,为什么堆栈溢出发生? (注意:我在Ubuntu上使用Hugs)

oddSum 20000 错误 - 控制堆栈溢出

oddSum 100000 错误 - 垃圾收集无法回收足够的空间

2 个答案:

答案 0 :(得分:8)

 oddSum 3
 oddSum 2 ((2 mod 2)*2 + 3)
 oddSum 1 ((1 mod 2)*1 + ((2 mod 2)*2 + 3))

您正在result变量中构建一个巨大的thunk。 一旦你评估了这一点,所有的计算必须立即完成,然后堆栈溢出,因为,为了执行加法,例如,你首先必须评估操作数,以及操作数中的加法操作数。

如果,otoh,thunk变得太大,你就会出现堆溢出。

尝试使用

result `seq` ((mod n 2) * n + result)

在递归中。

答案 1 :(得分:8)

首先,不要使用Hugs,它不受支持。通过优化GHC,可以将这样的事情编译成一个紧密有效的循环(仍然你的代码不会很好)。

非严格的累积器总是会冒成巨大的风险。一种解决方案是严格要求:

{-# LANGUAGE BangPatterns   #-}

oddSum' n !acc | n==0       = acc
               | otherwise  = oddSum' (n-1) $ (n`mod`2)*n + acc

当然,这几乎不是惯用的;显式编写尾递归函数是麻烦的,在Haskell中有点不受欢迎。大多数这类事情可以很好地完成库函数,比如

oddSum n = sum [1, 3 .. n]

......遗憾的是,在恒定的空间中也不能可靠地工作。它确实适用于折叠的严格版本(sum仅仅是其特化),

import Data.List
oddSum n = foldl' (+) 0 [1, 3 .. n]