Risposte:
Dal post del blog " Tail calls, @tailrec and trampolines ":
- In Scala 2.8, sarai anche in grado di utilizzare la nuova
@tailrec
annotazione per ottenere informazioni su quali metodi sono ottimizzati.
Questa annotazione ti consente di contrassegnare metodi specifici che speri che il compilatore ottimizzi.
Riceverai quindi un avviso se non sono ottimizzati dal compilatore.- In Scala 2.7 o versioni precedenti, dovrai fare affidamento su test manuali, o ispezioni del bytecode, per capire se un metodo è stato ottimizzato.
Esempio:
potresti aggiungere
@tailrec
un'annotazione in modo da essere sicuro che le tue modifiche hanno funzionato.
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)
}
}
E funziona dal REPL (esempio dai suggerimenti e trucchi 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 = {
^
Il compilatore Scala ottimizzerà automaticamente qualsiasi metodo veramente ricorsivo di coda. Se annoti un metodo che ritieni sia ricorsivo in coda con l' @tailrec
annotazione, il compilatore ti avviserà se il metodo non è effettivamente ricorsivo in coda. Questo rende il file@tailrec
annotazione una buona idea, sia per garantire che un metodo sia attualmente ottimizzabile sia per mantenerlo ottimizzabile man mano che viene modificato.
Nota che Scala non considera un metodo ricorsivo in coda se può essere sovrascritto. Pertanto il metodo deve essere privato, finale, su un oggetto (al contrario di una classe o di un tratto) o all'interno di un altro metodo per essere ottimizzato.
L'annotazione è scala.annotation.tailrec
. Attiva un errore del compilatore se il metodo non può essere ottimizzato per la chiamata di coda, cosa che accade se:
Si trova appena prima di def
in una definizione di metodo. Funziona in REPL.
Qui importiamo l'annotazione e proviamo a contrassegnare un metodo come @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 {
^
Ops! L'ultima invocazione è 1.+()
, no length()
! Riformuliamo il metodo:
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
Notare che length0
è automaticamente privato perché è definito nell'ambito di un altro metodo.
override
annotazione in Java: il codice funziona senza di essa, ma se lo metti lì ti dice se hai fatto un errore.