延续可以用来替代递归吗?
以下函数针对 n = 5,000 生成“堆栈级别太深 (SystemStackError)”
def factorial(n)
n == 0 ? 1 : factorial(n -1) * n
end
有没有办法使用 Continuations/callcc 来避免此错误?
注意:
我知道这可以在没有递归的情况下实现。例如
def factorial2(n)
(1..n).inject(1) {|result, n| result * n }
end
The following function generates a 'stack level too deep (SystemStackError)' for n = 5,000
def factorial(n)
n == 0 ? 1 : factorial(n -1) * n
end
Is there a way to avoid this error using continuations/callcc?
Note:
I know this can be implemented without recursion. e.g.
def factorial2(n)
(1..n).inject(1) {|result, n| result * n }
end
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
当然。连续可以做任何事情!但是,您最终将重新发明以下两件事之一:循环或函数调用。在我的机器上,默认情况下进行尾部调用优化,带有 call/cc 的尾部递归没有得到优化。由于每个
callcc
显然都捕获了整个调用堆栈,因此程序很快就会陷入困境。该代码被破坏,这是一个 call/cc 循环:注意: 之前我忘记了 call/cc 不仅仅是启动一个连续传递链,并且对递归的需要感到困惑,因此有下面的评论。
Sure. Continuations can do anything! However, you'll end up reinventing one of two things: a loop or a function call. On my machine, which does tail-call optimization by default, tail recursion with call/cc does not get optimized. The program quickly bogs down as each
callcc
apparently captures the entire call stack. That code being broken, here is a call/cc loop:Note: previously I'd forgotten that call/cc isn't just about initiating a continuation-passing chain and got confused about the need for recursion, hence the comments below.
您可以使用
tailcall_optimize :factorial
启用尾调用优化,取自此处。有关确定是否启用尾递归的信息,请参阅这篇有趣的文章。
You can enable tail-call optimization with
tailcall_optimize :factorial
, taken from here.See this interesting post about determining if tail recursion is enabled.
问题是,该函数返回 Factorial(n -1) * n ,这是一个表达式,而不是单个递归调用。如果你设法在 return 语句中只包含函数调用,那么该函数被称为尾递归,并且一个好的编译器/解释器(我不确定 Ruby 是否有能力)可以做一些事情优化以避免堆栈的使用。
这样的尾递归函数可能看起来像这样,但请注意,我不是 Ruby 程序员,所以我既不习惯语法,也不习惯解释器的功能。但你也许可以自己尝试一下:
The problem is, that the function returns
factorial(n -1) * n
which is a expression and no single recursive call. If you manage to have only the function call in the return statement, then the function is called tail recursive, and a good compiler / interpreter (i am not sure if Ruby is capable of it) can do some optimizations to avoid the usage of the stack.Such a tail-recursive function might look like this, but please note that I'm not a Ruby programmer, so I am neither used to the syntax nor to the features of the interpreter. But you might be able to try it on your own: