如何避免Haskell中的堆栈溢出?

时间:2011-09-23 20:32:33

标签: haskell recursion stack-overflow

Haskell不支持循环计算,而是提供使用递归算法。但是这种方法导致堆栈增长,甚至堆栈溢出。我认为应该有办法解决这个问题。这是样本。我想知道,每5秒钟可以调用 getClockTime 多少次:

import System.Time

nSeconds = 5

main = do
    initTime <- totalPicoSeconds `fmap` getClockTime
    doWork initTime 1
    where
    doWork initTime n = do
        currTime <- totalPicoSeconds `fmap` getClockTime
        if (currTime - initTime) `div` 10 ^ 12 >= nSeconds
            then print n
            else doWork initTime (n+1)

totalPicoSeconds :: ClockTime -> Integer
totalPicoSeconds (TOD a b) = a * 10 ^ 12 + b

该程序耗时5秒,但最终我得到了:

  

堆栈空间溢出:当前大小为8388608字节   使用`+ RTS -Ksize -RTS'来增加它。

在特定情况下,手动管理堆栈大小可能会有所帮助,但如果我希望运行此算法10秒钟,它可能会再次溢出。所以这不是解决方案。我怎样才能使这段代码有效?

1 个答案:

答案 0 :(得分:31)

这里的问题不是递归,而是你的n参数的懒惰。 Haskell中的堆栈溢出来自于尝试评估深层嵌套的thunk;在你的情况下,每次调用doWork initTime (n+1)都会在第二个参数中产生一个稍微深度嵌套的thunk。只需严格要求,事情应该再次开心:doWork initTime $! n+1