Ruby是否执行尾调用优化?

22 浏览
0 Comments

Ruby是否执行尾调用优化?

函数式编程语言倾向于使用递归解决许多问题,因此许多函数式编程语言都执行尾调用优化(TCO)。TCO使得从一个函数(或者自身,这种情况下也被称为尾递归消除,是TCO的一个子集)的最后一步调用另一个函数时,不需要新的栈帧,从而减少了开销和内存使用。

显然,Ruby从函数式编程语言中“借用”了许多概念(例如lambda函数、诸如map等函数等),这让我好奇:Ruby是否执行尾调用优化?

0
0 Comments

Ruby是否执行尾调用优化?

尾调用优化是一种优化技术,可以在函数调用的尾部发生时,将其转化为跳转而不是真正的函数调用。这可以减少函数调用的开销,特别是在递归函数中。然而,在Ruby中,尾调用优化并不是默认开启的。

Ruby MRI(1.9、2.0和2.1)可以通过以下方式开启尾调用优化:

RubyVM::InstructionSequence.compile_option = {

:tailcall_optimization => true,

:trace_instruction => false

}

在Ruby 2.0中,有一个提案将尾调用优化默认开启。这个提案还解释了一些相关问题。从提案中摘录的一段话如下:

“通常,尾递归优化包括另一种优化技术 - 将‘调用’转化为‘跳转’。在我看来,这种优化很难实现,因为在Ruby的世界中很难识别‘递归’。

下面是一个例子。在‘else’子句中,fact()方法的调用就不是‘尾调用’。”

def fact(n)

if n < 2

1

else

n * fact(n-1)

end

end

如果想要在fact()方法中使用尾调用优化,可以按照以下方式修改fact()方法(使用延续传递风格):

def fact(n, r)

if n < 2

r

else

fact(n-1, n*r)

end

end

总结起来,Ruby并不默认执行尾调用优化。然而,可以通过设置来开启尾调用优化。尾调用优化可以减少函数调用的开销,特别是在递归函数中。尽管识别递归在Ruby中可能有一些困难,但通过修改函数的编写风格,可以实现尾调用优化。

0
0 Comments

Ruby不支持尾递归优化(Tail Call Optimization, TCO)。虽然Ruby语言规范没有明确说明关于TCO的问题,但也没有禁止实现TCO。所以你不能依赖于TCO。

与Scheme不同,Scheme的语言规范要求所有实现都必须支持TCO。但与之相反,Guido van Rossum多次明确表示Python不应该支持TCO。

Yukihiro Matsumoto对TCO持有同情态度,但他不希望强制所有实现都支持它。不幸的是,这意味着你不能依赖于TCO,或者如果你依赖于它,你的代码将无法在其他Ruby实现中运行。

因此,一些Ruby实现支持TCO,但大多数不支持。例如,YARV(Yet Another Ruby VM)支持TCO,尽管(目前)你需要在源代码中显式取消注释一行并重新编译VM以激活TCO - 在未来的版本中,TCO将默认开启,当实现证明稳定后。Parrot虚拟机原生支持TCO,因此Cardinal也很容易支持它。CLR对TCO有一定的支持,这意味着IronRuby和Ruby.NET可能也可以实现TCO。Rubinius也可能实现TCO。

但JRuby和XRuby不支持TCO,除非JVM本身支持TCO。问题是,如果你想要一个快速的实现,并且与Java的集成快速无缝,那么你应该与Java具有堆栈兼容性,并尽可能多地使用JVM的堆栈。你可以很容易地使用跳板或显式的传递风格实现TCO,但这样你就不再使用JVM堆栈,这意味着每次你想要调用Java或从Java调用Ruby时,都必须执行某种转换,这是很慢的。因此,XRuby和JRuby选择了速度和Java集成,而不是TCO和continuations(基本上有相同的问题)。

这适用于所有希望与不支持TCO的某些宿主平台紧密集成的Ruby实现。例如,我猜MacRuby也会遇到同样的问题。

我可能是错误的(如果是的话,请告诉我),但我怀疑TCO在真正的面向对象语言中没有任何意义,因为尾调用必须能够重用调用者的堆栈帧。由于在运行时不知道通过消息发送将调用哪个方法,因此很难确保这一点(也许通过类型反馈JIT,或者通过强制所有消息的实现者使用相同大小的堆栈帧,或者通过将TCO限制为相同消息的自我发送…)。

这是一个很好的回答。通过谷歌很难找到这些信息。有趣的是,yarv支持它。

Damien,事实证明,TCO实际上对于真正的面向对象语言是必需的:请参见projectfortress.sun.com/Projects/Community/blog/…。不要太担心堆栈帧的问题:可以很好地设计堆栈帧,使其与TCO良好地配合工作。

tonyg从灭绝中保存了GLS引用的帖子,将其镜像在这里:eighty-twenty.org/index.cgi/tech/oo-tail-calls-20111001.html

我正在做一个要求我反汇编任意深度的嵌套数组的作业。显而易见的方法是使用递归,而且在线上找到的类似用例(我能找到的)也使用递归。虽然即使没有TCO,我的特定问题很不可能出现问题,但是我不能写一个完全通用的解决方案而不转换为迭代,这让我感到困扰。

我似乎找不到TCO的任何缺点,为什么Python要强制不实现它?

stackoverflow.com/questions/13591970/… 简而言之:作者的观点,奇怪的回溯,对实现细节的不希望依赖。虽然我不同意这个观点,但这些是原因。

你不是重用堆栈帧,你只是移除旧的堆栈帧,因为你已经用完了。

哇,又一个原因证明Matz是最好的仁慈的独裁者。哥们,你再也找不到比他更好的了!

如果有人对Frank和Damien提到的文章感兴趣,这是一个新的链接,可以打开。这篇文章非常有趣,所以我强烈推荐!eighty-twenty.org/2011/10/01/oo-tail-calls

0
0 Comments

Ruby 2.0可以执行尾递归优化,但不保证一定会执行。可以通过源代码启用尾递归优化,而不需要进行C源代码的修改和重新编译。

在一个问题讨论中,某些情况下了Ruby是否执行尾递归优化。有人给出了一个链接,但该链接已经失效。虽然该bug已经有5年了,但在同一个主题上仍有相关活动。

有人回答说在Ruby 2.0中可以通过源代码启用尾递归优化,而不需要进行C源代码的修改和重新编译。

0