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

Dmi*_*lov 18 stack-overflow recursion haskell

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
Run Code Online (Sandbox Code Playgroud)

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

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

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

Dan*_*ner 33

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

  • PS Credit来自Brent Yorgey,他在三年前帮助我调试了我一些代码中几乎相同的错误。 (2认同)