什么是Scala注释来确保尾recursion函数被优化?
我认为有@tailrec
注释,以确保编译器将优化尾recursion函数。 你只是把它放在宣言前面? 如果在脚本模式下使用Scala(例如在REPL下使用:load <file>
),它也可以工作吗?
从“ 尾巴呼叫,@ tailrec和蹦床 ”博客文章:
- 在Scala 2.8中,您还可以使用新的
@tailrec
注释来获取有关优化哪些方法的信息。
这个注解可以让你标记你希望编译器优化的特定方法。
如果它们没有被编译器优化,你将会得到一个警告。- 在Scala 2.7或更早版本中,您将需要依靠手动testing或字节码检查来确定方法是否已经优化。
例:
你可以添加一个
@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提示和技巧中的例子)
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 = { ^
Scala编译器会自动优化任何真正的尾recursion方法。 如果您使用@tailrec
注释来注释一个您认为是尾recursion的@tailrec
,那么编译器会警告您该方法是否实际上不是尾recursion。 这使得@tailrec
注释是一个好主意,既可以确保方法当前可优化,并且在修改时仍然可以优化。
请注意,如果可以重写,Scala不会考虑一个方法是尾recursion的。 因此,该方法必须是私有的,最终的,在对象上(而不是类或特征),或者在另一种方法中被优化。
注释是scala.annotation.tailrec
。 如果该方法不能被尾调用优化,则会触发编译器错误,如果发生以下情况:
- recursion调用不在尾部位置
- 该方法可以被重写
- 该方法不是最终的(前面的特例)
它放在方法定义的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
是自动专用的,因为它是在另一个方法的范围内定义的。