【问题标题】:Why does this code cause a stack overflow?为什么这段代码会导致堆栈溢出?
【发布时间】:2009-04-18 07:20:44
【问题描述】:

以下会导致大'n'的堆栈溢出,我可以理解为什么。

def factorial(n)
  (n > 1) ? (return (n * factorial(n - 1))) : (return 1)
end

为什么下面的也会导致溢出?

def factorial(n, k)
  (n > 1) ? (return factorial(n - 1, lambda {|v| return k.call(v * n)})) : (return k.call(1))
end

【问题讨论】:

    标签: ruby recursion


    【解决方案1】:

    您的第二个算法创建了一个n-long lambda 过程链,每个过程都包含对前一个过程的引用。我不确切知道 Ruby 做了什么,但是在适当的尾递归语言中,堆栈不会在您的第二个算法中溢出,因为 lambda 中的 k.call 也在尾部位置。如果如 Brian 的实验所示,Ruby 没有适当的尾调用,那么当调用链的头部时,n-long 对 lambda 的嵌套调用链将溢出堆栈,即使 Ruby 足够聪明,可以转换将尾递归 factorial 调用到一个循环中(= 尾调用优化)。

    【讨论】:

    • 第二个版本仍在阶乘上递归。我以为 Ruby 没有做 TCO。但它不会破坏堆栈,直到它到达 lambdas。我很困惑。 (其他人更加困惑,或者没有阅读问题。我适当地对其他人投了反对票。)
    • “但它不会破坏堆栈,直到它到达 lambdas。” ——呃?我不明白。
    • 鉴于第二个示例仍在factorial 上递归,我预计它会在达到k.call(1) 的基本情况之前溢出堆栈。但是在上面的第二个示例代码中,对factorial 的调用不会溢出堆栈(即使对于非常大的 n),这与第一个示例的行为不同。第二个示例确实达到了该基本情况,并且在发生大量k.call 之前不会溢出。如果没有 TCO,我看不出它是如何做到这一点的。
    • 是的,我同意,必须存在某种形式的简单 TCO。相应地进行了编辑。
    • 澄清 - 1. 我认为我有一个正确的(如果不是最好的)CPS 样式 fn。如果我错了,那是什么错误? 2. 恕我直言,所有编程语言都会有某种形式的 TCO。如果像Anton之前所说的那样,那不应该防止堆栈溢出吗?所以 Ruby 确实“并且”没有 TCO?我忽略了什么?
    【解决方案2】:

    在大多数语言中,函数调用进入调用堆栈,这实际上只是堆栈。递归调用函数会不断添加到调用堆栈中。最终你填满了堆栈,你会得到堆栈溢出。在运行递归函数时,您的递归级别会很深,这始终是一种危险。

    【讨论】:

    • -1:参见。 “对于大的 'n',以下内容会溢出,我可以理解为什么”
    【解决方案3】:

    出于同样的原因,第一个有堆栈溢出...调用堆栈变得太大。

    【讨论】:

      【解决方案4】:

      与第一个函数一样,递归调用最终可能会导致系统无法处理。

      【讨论】:

        猜你喜欢
        • 2014-12-20
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2011-01-13
        • 2018-07-01
        • 1970-01-01
        • 1970-01-01
        • 2016-02-24
        相关资源
        最近更新 更多