【问题标题】:Does Scala support tail recursion optimization?Scala 是否支持尾递归优化?
【发布时间】:2009-11-04 23:35:47
【问题描述】:

Scala 是否支持尾递归优化?

【问题讨论】:

    标签: performance scala tail-recursion


    【解决方案1】:

    正如其他发帖人所说,Scala 在编译时进行尾递归优化。也就是说,一个尾递归函数被编译器转化为一个循环(方法调用被转化为一个跳转),从运行尾递归函数时的堆栈跟踪可以看出。

    试试下面的 sn-p:

    def boom(n: Int): Nothing = if(n<=0) throw new Exception else boom(n-1)
    boom(10)
    

    并检查堆栈跟踪。它只会显示对函数 boom 的一次调用——因此编译后的字节码不是递归的。

    有一个关于implement tail calls at the JVM level 的提议——在我看来这是一件很棒的事情,因为这样 JVM 可以进行运行时优化,而不仅仅是代码的编译时优化——并且可能意味着更灵活尾递归。基本上,tailcall invoke 的行为与普通方法 invoke 完全相同,但会在安全的情况下丢弃调用者的堆栈 - JVM 的规范规定必须保留堆栈帧,因此 JIT 必须做一些事情静态代码分析以确定堆栈帧是否永远不会被使用。

    它的当前状态是proto 80%。我认为 Java 7 不会及时完成(invokedynamic 具有更高的优先级,实现几乎完成)但 Java 8 可能会看到它实现。

    【讨论】:

    • "它的当前状态是 proto 80%"。我不明白。我以为 Arnold Schwaighofer 几年前在 John Rose 的指导下完全实现了这一点?
    • @JanHarrop 也许那是关于尾递归而不是一般尾调用?
    • @Cubic:不,这是一般的尾声。 Arnold 还在 LLVM 中实现了它们。
    【解决方案2】:

    在 Scala 2.8 中,您可以使用 @tailrec 标记您希望编译器会优化的特定方法:

    import scala.annotation.tailrec
    
    @tailrec def factorialAcc(acc: Int, n: Int): Int = {
      if (n <= 1) acc
      else factorialAcc(n * acc, n - 1)
    }
    

    如果无法优化方法,则会出现编译时错误。

    【讨论】:

    • 需要用“import scala.annotation.tailrec”导入注解
    【解决方案3】:

    Scala 2.7.x 支持对 final 方法和局部函数的自递归(一个函数调用自身)进行尾调用优化。

    Scala 2.8 也可能带有对 trampoline 的库支持,这是一种优化相互递归函数的技术。

    关于 Scala 递归状态的大量信息可以在 Rich Dougherty's blog 中找到。

    【讨论】:

    【解决方案4】:

    仅在函数是自递归的非常简单的情况下。

    Proof of tail recursion ability.

    不过,看起来 Scala 2.8 可能会改进尾递归识别。

    【讨论】:

    • “仅在函数自递归的非常简单的情况下。”:这是否意味着当使用延续时,很容易耗尽堆栈空间?
    猜你喜欢
    • 1970-01-01
    • 2012-11-15
    • 2011-09-04
    • 2014-09-22
    • 2017-07-23
    • 1970-01-01
    • 1970-01-01
    • 2011-01-25
    • 2014-06-23
    相关资源
    最近更新 更多