什么是Scala注释以确保优化尾递归函数?

时间:2010-06-24 21:48:21

标签: scala tail-call-optimization

我认为有@tailrec注释可确保编译器优化尾递归函数。你刚才把它放在宣言面前吗?如果Scala用于脚本模式(例如在REPL下使用:load <file>),它是否也有效?

3 个答案:

答案 0 :(得分:107)

来自“Tail calls, @tailrec and trampolines”博文:

  
      
  • 在Scala 2.8中,您还可以使用新的@tailrec注释来获取有关哪些方法已优化的信息。
      此注释允许您标记希望编译器优化的特定方法   如果编译器没有优化它们,您将收到警告。
  •   
  • 在Scala 2.7或更早版本中,您需要依靠手动测试或检查字节码来确定方法是否已经过优化。
  •   

示例:

  

您可以添加@tailrec注释,以确保您的更改有效。

import scala.annotation.tailrec

class Factorial2 {
  def factorial(n: Int): Int = {
    @tailrec def factorialAcc(acc: Int, n: Int): Int = {
      if (n <= 1) acc
      else factorialAcc(n * acc, n - 1)
    }
    factorialAcc(1, n)
  }
}

它起作用于REPL(来自Scala REPL tips and tricks的例子):

C:\Prog\Scala\tests>scala
Welcome to Scala version 2.8.0.RC5 (Java HotSpot(TM) 64-Bit Server VM, Java 1.6.0_18).
Type in expressions to have them evaluated.
Type :help for more information.

scala> import scala.annotation.tailrec
import scala.annotation.tailrec

scala> class Tails {
     | @tailrec def boom(x: Int): Int = {
     | if (x == 0) throw new Exception("boom!")
     | else boom(x-1)+ 1
     | }
     | @tailrec def bang(x: Int): Int = {
     | if (x == 0) throw new Exception("bang!")
     | else bang(x-1)
     | }
     | }
<console>:9: error: could not optimize @tailrec annotated method: it contains a recursive call not in tail position
       @tailrec def boom(x: Int): Int = {
                    ^
<console>:13: error: could not optimize @tailrec annotated method: it is neither private nor final so can be overridden
       @tailrec def bang(x: Int): Int = {
                    ^

答案 1 :(得分:35)

Scala编译器将自动优化任何真正的尾递归方法。如果您使用@tailrec注释注释一个您认为是尾递归的方法,那么编译器将警告您该方法实际上是否为尾递归。这使得@tailrec注释成为一个好主意,既可以确保方法当前是可优化的,也可以在修改时保持优化。

请注意,如果可以覆盖该方法,则Scala不会将该方法视为尾递归。因此,该方法必须是私有的,最终的,对象(与类或特征相对),或者是要优化的另一个方法。

答案 2 :(得分:21)

注释为scala.annotation.tailrec。如果方法不能进行尾调用优化,则会触发编译器错误,如果出现以下情况,则会发生以下情况:

  1. 递归调用不在尾部位置
  2. 该方法可以覆盖
  3. 该方法不是最终的(前面的特例)
  4. 它位于方法定义中的def之前。它适用于REPL。

    这里我们导入注释,并尝试将方法标记为@tailrec

    scala> import annotation.tailrec
    import annotation.tailrec
    
    scala> @tailrec def length(as: List[_]): Int = as match {  
         |   case Nil => 0
         |   case head :: tail => 1 + length(tail)
         | }
    <console>:7: error: could not optimize @tailrec annotated method: it contains a recursive call not in tail position
           @tailrec def length(as: List[_]): Int = as match { 
                        ^
    

    糟糕!最后一次调用是1.+(),而不是length()!让我们重新制定方法:

    scala> def length(as: List[_]): Int = {                                
         |   @tailrec def length0(as: List[_], tally: Int = 0): Int = as match {
         |     case Nil          => tally                                       
         |     case head :: tail => length0(tail, tally + 1)                    
         |   }                                                                  
         |   length0(as)
         | }
    length: (as: List[_])Int
    

    请注意length0自动为私有,因为它是在另一种方法的范围内定义的。