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
程序运行 5 秒,但最终我得到:
堆栈空间溢出:当前大小 8388608 字节。
使用“+RTS -Ksize -RTS”来增加它。
手动管理堆栈大小可能在特定情况下有所帮助,但如果我希望运行此算法 10 秒,它可能会再次溢出。所以这不是一个解决方案。我怎样才能让这段代码工作?
Haskell does not support cycling for computation, instead it offers to use recursion algorithms. But this approach leads to growing of stack, and even stack overflow. I believe there should be approach to solve this problem in general. Here is the sample. I wanted to know, how many times getClockTime may be called per 5 seconds:
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
The program goes 5 seconds, but eventually I'm getting:
Stack space overflow: current size 8388608 bytes.
Use `+RTS -Ksize -RTS' to increase it.
Manual management of stack size may help in particular case, but if I would wish to run this algo for 10 seconds, it may overflow again. So this is not a solution. How can I get this code working?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
这里的问题不是递归,而是你的 n 参数的惰性。 Haskell 中的堆栈溢出来自于尝试评估深层嵌套的 thunk;在您的情况下,每次调用
doWork initTime (n+1)
都会在第二个参数中进行稍深嵌套的重击。只需将其严格化,事情就会再次愉快:doWork initTime $! n+1 。
The problem here is not the recursion, but the laziness of your
n
argument. Stack overflows in Haskell come from trying to evaluate deeply-nested thunks; in your case, each call todoWork initTime (n+1)
is making a slightly-deeperly-nested thunk in the second argument. Simply make it strict, and things should be happy again:doWork initTime $! n+1
.