Answers:
从“ 尾巴,@ tailrec和蹦床 ”博客文章中:
- 在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技巧和窍门中的示例):
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编译器将自动优化任何真正的尾递归方法。如果使用注释对您认为是尾递归的方法进行@tailrec
注释,则编译器会在该方法实际上不是尾递归的情况下向您发出警告。这使@tailrec
注释成为一个好主意,既可以确保方法当前可优化,又可以在修改方法后使其保持优化。
请注意,如果可以覆盖,Scala不会认为该方法是尾递归的。因此,该方法必须是私有的,最终的,在对象上(而不是类或特征),或在要优化的另一种方法内部。
注释为scala.annotation.tailrec
。如果该方法无法进行尾部调用优化,则会触发编译器错误,如果发生以下情况,则会发生此错误:
它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
是自动私有的,因为它是在另一种方法的范围内定义的。
override
Java中的注释-代码可以在没有注释的情况下工作,但是如果您将其放在此处,它会告诉您是否出错。