延续可以用来替代递归吗?

发布于 2024-08-25 14:06:33 字数 333 浏览 5 评论 0原文

以下函数针对 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 技术交流群。

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

发布评论

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

评论(3

糖粟与秋泊 2024-09-01 14:06:33

当然。连续可以做任何事情!但是,您最终将重新发明以下两件事之一:循环或函数调用。在我的机器上,默认情况下进行尾部调用优化,带有 call/cc 的尾部递归没有得到优化。由于每个 callcc 显然都捕获了整个调用堆栈,因此程序很快就会陷入困境。该代码被破坏,这是一个 call/cc 循环:

def fact( n )
    (n, f, k) = callcc { |k| [ n, 1, k ] }
    if ( n == 0 ) then return f
    else k.call n-1, n*f, k
    end
end

注意: 之前我忘记了 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:

def fact( n )
    (n, f, k) = callcc { |k| [ n, 1, k ] }
    if ( n == 0 ) then return f
    else k.call n-1, n*f, k
    end
end

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.

狼亦尘 2024-09-01 14:06:33

您可以使用 tailcall_optimize :factorial 启用尾调用优化,取自此处

class Class
  def tailcall_optimize( *methods )
    methods.each do |meth|
      org = instance_method( meth )
      define_method( meth ) do |*args|
        if Thread.current[ meth ]
          throw( :recurse, args )
        else
          Thread.current[ meth ] = org.bind( self )
          result = catch( :done ) do
            loop do
              args = catch( :recurse ) do
                throw( :done, Thread.current[ meth ].call( *args ) )
              end
            end
          end
          Thread.current[ meth ] = nil
          result
        end
      end
    end
  end
end

有关确定是否启用尾递归的信息,请参阅这篇有趣的文章

You can enable tail-call optimization with tailcall_optimize :factorial, taken from here.

class Class
  def tailcall_optimize( *methods )
    methods.each do |meth|
      org = instance_method( meth )
      define_method( meth ) do |*args|
        if Thread.current[ meth ]
          throw( :recurse, args )
        else
          Thread.current[ meth ] = org.bind( self )
          result = catch( :done ) do
            loop do
              args = catch( :recurse ) do
                throw( :done, Thread.current[ meth ].call( *args ) )
              end
            end
          end
          Thread.current[ meth ] = nil
          result
        end
      end
    end
  end
end

See this interesting post about determining if tail recursion is enabled.

酒解孤独 2024-09-01 14:06:33

问题是,该函数返回 Factorial(n -1) * n ,这是一个表达式,而不是单个递归调用。如果你设法在 return 语句中只包含函数调用,那么该函数被称为尾递归,并且一个好的编译器/解释器(我不确定 Ruby 是否有能力)可以做一些事情优化以避免堆栈的使用。

这样的尾递归函数可能看起来像这样,但请注意,我不是 Ruby 程序员,所以我既不习惯语法,也不习惯解释器的功能。但你也许可以自己尝试一下:

def factorial(n, result=1)
    n == 0 ? result : factorial(n-1, result * n)
end

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:

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