39

在 stackoverflow.com 上发布堆栈溢出问题,多么有趣:-)

我正在运行一些递归 Ruby 代码,我得到:"Stack level too deep (SystemStackError)"

(我很确定代码有效,我没有处于无限递归的死亡螺旋中,但这不是重点)

无论如何可以更改我的 Ruby 应用程序允许的堆栈深度/大小?

如果这是 Ruby 中的限制,我不太明白,因为错误显示“堆栈级别”,这给我的印象是 Ruby 以某种方式计算堆栈的“级别”,或者它只是意味着堆栈已满。

我试过在 Vista 和 Ubuntu 下运行这个程序,结果相同。在 Ubuntu 下,我尝试使用 'ulimit -s' 将堆栈大小从 8192 更改为 16000,但这并没有改变任何东西。

编辑:感谢您的反馈。
我确实意识到使用递归函数可能不是最可靠的方法。但这也不是重点。我只是想知道是否有办法增加堆栈大小......期间。正如我所提到的,我确实在运行 ruby​​ 脚本之前尝试运行 ulimit -s 16000 ..没有任何改进..我用错了吗?

Edit2:实际上,我在代码的边缘情况下进行了无限递归。
收到错误时截断的 ruby​​ 堆栈跟踪"Stack level too deep"有点误导。
当具有涉及多个函数的递归行为时,您会得到递归数量比实际数量少得多的印象。在此示例中,可能会在超过 190 次调用后崩溃,但实际上大约是 15000 次调用

tst.rb:8:in `p': stack level too deep (SystemStackError)
        from tst.rb:8:in `bar'
        from tst.rb:12:in `bar'
        from tst.rb:19:in `foo'
        from tst.rb:10:in `bar'
        from tst.rb:19:in `foo'
        from tst.rb:10:in `bar'
        from tst.rb:19:in `foo'
        from tst.rb:10:in `bar'
         ... 190 levels...
        from tst.rb:19:in `foo'
        from tst.rb:10:in `bar'
        from tst.rb:19:in `foo'
        from tst.rb:22

-安德烈亚斯

4

7 回答 7

27

这个问题及其答案似乎可以追溯到使用 C 堆栈的 Ruby 1.8.x。Ruby 1.9.x 及更高版本使用具有自己堆栈的 VM。在 Ruby 2.0.0 及更高版本中,VM 堆栈的大小可以通过RUBY_THREAD_VM_STACK_SIZE环境变量进行控制。

于 2014-12-16T17:29:04.320 回答
13

如果您确定没有无限递归情况,那么您的算法可能不适合 Ruby 以递归方式执行它。将算法从递归转换为不同类型的堆栈非常容易,我建议您尝试一下。这是你如何做到的。

def recursive(params)
  if some_conditions(params)
     recursive(update_params(params))
  end
end

recursive(starting_params)

将转变为

stack = [starting_params]
while !stack.empty?
  current_params = stack.delete_at(0)
  if some_conditions(current_params)
    stack << update_params(current_params)
  end
end
于 2008-10-28T18:38:35.323 回答
8

Yukihiro Matsumoto 在这里写

Ruby 使用 C 堆栈,因此您需要使用 ulimit 来指定堆栈深度的限制。

于 2008-10-28T09:32:15.817 回答
7

Ruby 使用 C 堆栈,因此您的选项包括使用 ulimit 或使用一些编译器/链接器堆栈大小标志编译 Ruby。尾递归尚未实现,Ruby 当前对递归的支持不是很好。由于递归很酷且优雅,您可能需要考虑应对语言的限制并以不同的方式编写代码。

于 2008-10-28T09:35:38.383 回答
3

想想代码发生了什么。正如其他海报所提到的,可以破解解释器的 C 代码。然而。结果将是您正在使用更多的 RAM,并且无法保证您不会再次破坏堆栈。

真正好的解决方案是为您尝试做的事情提出一个迭代算法。有时记忆会有所帮助,有时你会发现你没有使用你正在压栈的东西,在这种情况下,你可以用可变状态替换递归调用。

如果您对这类东西不熟悉,请在此处查看 SICP以获得一些想法...

于 2008-10-28T11:50:53.650 回答
3

刚遇到同样的问题,在 Linux 或 Mac 上很容易修复。如其他答案所述,Ruby 使用系统堆栈设置。您可以通过设置堆栈大小在 Mac 和 Linux 上轻松更改此设置。狐狸示例:

ulimit -s 20000
于 2011-11-14T09:43:16.310 回答
1

Ruby 1.9.2开始,您可以通过以下方式打开尾调用优化:

RubyVM::InstructionSequence.compile_option = {
  tailcall_optimization: true,
  trace_instruction: false
}

RubyVM::InstructionSequence.new(<<-EOF).eval
  def me_myself_and_i
    me_myself_and_i
  end
EOF
me_myself_and_i # Infinite loop, not stack overflow

SystemStackError如果递归调用位于方法的末尾并且仅位于方法的末尾,这将避免错误。当然,这个例子会导致一个无限循环。在进行深度递归之前,最好使用浅递归(并且不进行优化)进行调试。

于 2017-05-08T00:31:27.537 回答