Haskell中如何避免堆栈溢出?

发布于 2024-12-06 00:34:36 字数 779 浏览 2 评论 0原文

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 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

鸵鸟症 2024-12-13 00:34:36

这里的问题不是递归,而是你的 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 to doWork 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.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文