it-swarm.com.de

Wie lautet die Anmerkung Scala), um sicherzustellen, dass eine rekursive Schwanzfunktion optimiert ist?

Ich denke, dort ist @tailrec Annotation, um sicherzustellen, dass der Compiler eine rekursive Tail-Funktion optimiert. Stellen Sie es einfach vor die Erklärung? Funktioniert es auch, wenn Scala im Skriptmodus verwendet wird (zum Beispiel mit :load <file> unter REPL)?

90
huynhjl

Aus dem Blog-Beitrag " Tail Calls, @tailrec und Trampoline ":

  • In Scala 2.8 können Sie auch die neue Annotation @tailrec Verwenden, um Informationen darüber zu erhalten, welche Methoden optimiert wurden.
    Mit dieser Annotation können Sie bestimmte Methoden markieren, die der Compiler hoffentlich optimieren wird.
    Sie erhalten dann eine Warnung, wenn sie nicht vom Compiler optimiert wurden.
  • In Scala 2.7 oder früher müssen Sie manuell testen oder den Bytecode überprüfen, um herauszufinden, ob eine Methode optimiert wurde.

Beispiel:

sie können eine @tailrec - Anmerkung hinzufügen, um sicherzugehen, dass Ihre Änderungen erfolgreich waren.

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 aus dem REPL (Beispiel aus dem Scala REPL Tipps und 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 = {
                    ^
111
VonC

Der Scala Compiler optimiert automatisch jede wirklich rekursive Methode. Wenn Sie eine Methode, von der Sie glauben, dass sie rekursiv ist, mit dem @tailrec Annotation, dann warnt Sie der Compiler, wenn die Methode tatsächlich nicht rekursiv ist. Dies macht die @tailrec Annotation ist eine gute Idee, um sicherzustellen, dass eine Methode derzeit optimiert werden kann und auch während der Änderung optimiert bleibt.

Beachten Sie, dass Scala eine Methode nicht als rekursiv ansieht, wenn sie überschrieben werden kann. Daher muss die Methode für ein Objekt entweder privat oder endgültig sein (im Gegensatz zu einer Klasse oder einem Merkmal). oder innerhalb einer anderen zu optimierenden Methode.

38
Dave Griffith

Die Anmerkung lautet scala.annotation.tailrec. Es löst einen Compiler-Fehler aus, wenn die Methode nicht für Tail-Calls optimiert werden kann. Dies geschieht, wenn:

  1. Der rekursive Aufruf befindet sich nicht in der Endposition
  2. Die Methode könnte überschrieben werden
  3. Die Methode ist nicht endgültig (Sonderfall der vorhergehenden)

Es wird in einer Methodendefinition direkt vor dem def platziert. Es funktioniert in der REPL.

Hier importieren wir die Annotation und versuchen, eine Methode als @tailrec Zu markieren.

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()! Formulieren wir die Methode neu:

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 length0 Automatisch privat ist, da es im Rahmen einer anderen Methode definiert wird.

23
retronym