据我所知,由于函数调用的开销,递归函数通常效率低于等效的非递归函数。但是,我最近遇到了一本教科书,说Java(和C#)不一定是这样。
它没有说明原因,但我认为这可能是因为Java编译器以某种方式优化了递归函数。
有谁知道为什么会这样的细节?
答案 0 :(得分:5)
教科书可能是指尾调优化;有关详细信息,请参阅@ Travis的答案。
但是,在Java的上下文中,教科书是不正确的。当前的Java编译器没有实现尾调用优化,显然是因为它会干扰Java安全实现,并且会出于各种目的改变对调用堆栈进行内省的应用程序的行为。
参考文献:
有一些提示,尾部调用优化可能会进入Java 8。
答案 1 :(得分:4)
这通常仅适用于尾递归(http://en.wikipedia.org/wiki/Tail_call)。
尾递归在语义上等同于递增的循环,因此可以优化为循环。以下是我与之相关的文章(强调我的):
尾巴通话很重要,因为 它们可以不加添加地实现 调用堆栈的新堆栈帧。 当前的大部分框架 不再需要程序,并且 它可以被替换为框架 尾调用,适当修改。 然后该程序可以跳转到 叫做子程序。生成这样的代码 而不是标准的呼叫序列 称为尾部呼叫消除,或尾部 通话优化。 在函数式编程语言中, 通常消除尾部呼叫 由语言标准保证, 这个保证允许使用 递归,特别是尾巴 递归,代替循环
答案 2 :(得分:4)
在某些情况下递归实现与迭代实现一样有效的一些原因:
然而,我的一般建议是不要担心这一点 - 差异太小,不太可能对您的整体表现产生影响。
答案 3 :(得分:2)
Java的父亲之一盖伊斯蒂尔于1977年撰写了一篇论文
Debunking the "Expensive Procedure Call" Myth
or, Procedure Call Implementations Considered Harmful
or, LAMBDA: The Ultimate GOTO
摘要: Folklore声明GOTO声明是 “便宜”,而程序调用“很贵”。这个 神话很大程度上是由于语言设计不佳造成的 的实施方式。
这很有趣,因为即使在今天,Java也没有尾调用优化:)
答案 4 :(得分:1)
据我所知,Java 不进行任何类型的递归优化。知道这一点很重要 - 不是因为效率,而是因为过度深度的递归(几千人应该这样做)会导致堆栈溢出并导致程序崩溃。 (真的,考虑到这个网站的名称,我很惊讶没有人在我面前提起这件事。)
答案 5 :(得分:0)