Antworten:
Aus dem Blog-Beitrag " Tail Calls, @tailrec und Trampoline ":
- In Scala 2.8 können Sie auch die neue
@tailrec
Anmerkung verwenden, um Informationen darüber zu erhalten, welche Methoden optimiert sind.
Mit dieser Anmerkung können Sie bestimmte Methoden markieren, die der Compiler hoffentlich optimieren wird.
Sie erhalten dann eine Warnung, wenn sie vom Compiler nicht optimiert wurden.- In Scala 2.7 oder früheren Versionen müssen Sie sich auf manuelle Tests oder die Überprüfung des Bytecodes verlassen, um herauszufinden, ob eine Methode optimiert wurde.
Beispiel:
Sie können eine
@tailrec
Anmerkung hinzufügen , um sicherzugehen, dass Ihre Änderungen funktioniert haben.
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)
}
}
Und es funktioniert mit der REPL (Beispiel aus den Tipps und Tricks der 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 = {
^
Der Scala-Compiler optimiert automatisch jede wirklich rekursive Methode. Wenn Sie eine Methode mit @tailrec
Anmerkungen versehen, von denen Sie glauben, dass sie mit der Anmerkung endrekursiv ist, werden Sie vom Compiler gewarnt, wenn die Methode tatsächlich nicht rekursiv ist. Dies macht die @tailrec
Annotation zu einer guten Idee, um sicherzustellen, dass eine Methode derzeit optimierbar ist und dass sie beim Ändern optimierbar bleibt.
Beachten Sie, dass Scala eine Methode nicht als schwanzrekursiv betrachtet, wenn sie überschrieben werden kann. Daher muss die Methode entweder privat, endgültig, für ein Objekt (im Gegensatz zu einer Klasse oder einem Merkmal) oder innerhalb einer anderen zu optimierenden Methode sein.
Die Anmerkung ist scala.annotation.tailrec
. Es löst einen Compilerfehler aus, wenn die Methode nicht optimiert werden kann. Dies geschieht, wenn:
Es wird def
in einer Methodendefinition direkt vor dem platziert . Es funktioniert in der REPL.
Hier importieren wir die Annotation und versuchen, eine Methode als zu markieren @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 {
^
Hoppla! Der letzte Aufruf ist 1.+()
nicht length()
! Lassen Sie uns die Methode neu formulieren:
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
Beachten Sie, dass dies length0
automatisch privat ist, da es im Bereich einer anderen Methode definiert wird.
override
Annotation in Java - der Code funktioniert ohne ihn, aber wenn Sie ihn dort ablegen, erfahren Sie, ob Sie einen Fehler gemacht haben.