代码之家  ›  专栏  ›  技术社区  ›  huynhjl bhericher

什么是scala注释以确保尾部递归函数得到优化?

  •  86
  • huynhjl bhericher  · 技术社区  · 16 年前

    我想有 @tailrec 注释以确保编译器将优化尾部递归函数。你把它放在申报单前面了吗?如果scala在脚本模式下使用(例如使用 :load <file> 在RePL下?

    3 回复  |  直到 9 年前
        1
  •  105
  •   VonC    16 年前

    从“ Tail calls, @tailrec and trampolines 博客帖子:

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

    例子:

    你可以添加一个 @泰瑞克 注释,以便确保更改有效。

    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 = {
                        ^
    
        2
  •  35
  •   Philippe    14 年前

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

    注意,如果一个方法可以被重写,scala不认为它是尾部递归的。因此,该方法必须是私有的、最终的、在对象上(与类或特性相反),或者在另一个要优化的方法内部。

        3
  •  21
  •   Ohashi    9 年前

    注释是 scala.annotation.tailrec . 如果方法不能进行尾调用优化,则会触发编译器错误,如果:

    1. 递归调用不在尾部位置
    2. 方法可以被重写
    3. 方法不是最终的(前面的特殊情况)

    它就放在 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 是自动私有的,因为它是在另一个方法的作用域中定义的。

    推荐文章