Was ist die Scala-Annotation, um sicherzustellen, dass eine rekursive Schwanzfunktion optimiert wird?
98
Ich denke, es gibt @tailrecAnmerkungen, um sicherzustellen, dass der Compiler eine rekursive Endfunktion optimiert. Stellen Sie es einfach vor die Erklärung? Funktioniert es auch, wenn Scala im Skriptmodus verwendet wird (z. B. :load <file>unter REPL)?
In Scala 2.8 können Sie auch die neue @tailrecAnmerkung 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 @tailrecAnmerkung hinzufügen , um sicherzugehen, dass Ihre Änderungen funktioniert haben.
C:\Prog\Scala\tests>scala
Welcome to Scala version 2.8.0.RC5 (JavaHotSpot(TM)64-BitServer VM,Java1.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>classTails{|@tailrec def boom(x:Int):Int={|if(x ==0)thrownewException("boom!")|else boom(x-1)+1|}|@tailrec def bang(x:Int):Int={|if(x ==0)thrownewException("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 @tailrecAnmerkungen 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 @tailrecAnnotation 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.
Ich nehme an, dies ähnelt der overrideAnnotation in Java - der Code funktioniert ohne ihn, aber wenn Sie ihn dort ablegen, erfahren Sie, ob Sie einen Fehler gemacht haben.
Zoltán
23
Die Anmerkung ist scala.annotation.tailrec. Es löst einen Compilerfehler aus, wenn die Methode nicht optimiert werden kann. Dies geschieht, wenn:
Der rekursive Aufruf befindet sich nicht in der Endposition
Die Methode könnte überschrieben werden
Die Methode ist nicht endgültig (Sonderfall der vorhergehenden)
Es wird defin 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{|caseNil=>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{|caseNil=> tally
|case head :: tail => length0(tail, tally +1)|}| length0(as)|}
length:(as:List[_])Int
Beachten Sie, dass dies length0automatisch privat ist, da es im Bereich einer anderen Methode definiert wird.
Scala kann Tail Calls nur für eine einzige Methode optimieren. Gegenseitig rekursive Aufrufe werden nicht optimiert.
Rich Dougherty
Ich hasse es, nicht wählerisch zu sein, aber in Ihrem Beispiel im Fall Nil sollten Sie Tally für eine korrekte Listenlängenfunktion zurückgeben, da Sie sonst nach Abschluss der Rekursion immer 0 als Rückgabewert erhalten.
override
Annotation in Java - der Code funktioniert ohne ihn, aber wenn Sie ihn dort ablegen, erfahren Sie, ob Sie einen Fehler gemacht haben.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
.Hoppla! Der letzte Aufruf ist
1.+()
nichtlength()
! Lassen Sie uns die Methode neu formulieren:Beachten Sie, dass dies
length0
automatisch privat ist, da es im Bereich einer anderen Methode definiert wird.quelle