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秒钟,它可能会再次溢出。所以这不是解决方案。我怎样才能使这段代码有效?
答案 0 :(得分:31)
这里的问题不是递归,而是你的n
参数的懒惰。 Haskell中的堆栈溢出来自于尝试评估深层嵌套的thunk;在你的情况下,每次调用doWork initTime (n+1)
都会在第二个参数中产生一个稍微深度嵌套的thunk。只需严格要求,事情应该再次开心:doWork initTime $! n+1
。