在 stackoverflow.com 上发布堆栈溢出问题,多有趣:-)
我正在运行一些递归 Ruby 代码,我得到:
"Stack level too deep (SystemStackError)"
(我很确定代码是有效的,我没有处于无限递归的死亡螺旋中,但这不是重点)
是否有办法更改我的 Ruby 应用程序允许的堆栈深度/大小?
我不太明白这是否是 Ruby 中的限制,因为错误显示“堆栈级别”,这给我的印象是 Ruby 以某种方式计算堆栈的“级别”,或者它只是意味着堆栈已满.
我尝试在 Vista 和 Ubuntu 下运行这个程序,结果相同。 在 Ubuntu 下,我尝试使用“ulimit -s”将堆栈大小从 8192 更改为 16000,但这并没有改变任何内容。
编辑:
感谢您的反馈。
我确实意识到使用递归函数可能不是最可靠的方法。
但这也不是重点。
我只是想知道是否有办法增加堆栈大小......期间。
正如我提到的,我确实尝试在运行 ruby 脚本之前运行 ulimit -s 16000 ..没有任何改进..我使用它是错误的吗?
编辑2:
事实上,我在代码的边缘情况下有无限递归。
当您收到
"Stack level too deep"
错误时,截断的 ruby 堆栈跟踪有点误导。 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
这个问题及其答案似乎可以追溯到 Ruby 1.8.x,它使用 C 堆栈。 Ruby 1.9.x 及更高版本使用具有自己的堆栈的 VM。在 Ruby 2.0.0 及更高版本中,可以通过
RUBY_THREAD_VM_STACK_SIZE
环境变量控制 VM 堆栈的大小。
如果您确定没有无限递归情况,那么您的算法可能不适合 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
松本幸弘写于这里
Ruby 使用 C 堆栈,因此您需要 使用 ulimit 指定限制 堆栈深度。
Ruby 使用 C 堆栈,因此您的选择包括使用 ulimit 或使用某些编译器/链接器堆栈大小标志来编译 Ruby。尾递归尚未实现,Ruby 目前对递归的支持还不是很好。由于递归很酷且优雅,您可能需要考虑应对语言的限制并以不同的方式编写代码。
想想代码发生了什么。正如其他发帖者所提到的,破解解释器的 C 代码是可能的。然而。结果将是您使用更多的 RAM 并且不能保证您不会再次破坏堆栈。
真正好的解决方案是为您想要做的事情提出一个迭代算法。有时记忆会有所帮助,有时您会发现自己没有使用推入堆栈的内容,在这种情况下,您可以用可变状态替换递归调用。
如果您对此类内容不熟悉,请查看 SICP here 获取一些想法...
刚刚遇到了同样的问题,在 Linux 或 Mac 上很容易修复。正如其他答案中所述,Ruby 使用系统堆栈设置。您可以通过设置堆栈大小在 Mac 和 Linux 上轻松更改此设置。狐狸的例子:
ulimit -s 20000
从 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
错误。当然,这个例子将导致无限循环。在进行深度递归之前,最好使用浅层递归(并且不进行优化)进行调试。