Respostas:
Da postagem do blog " Tail calls, @tailrec and trampolines ":
- No Scala 2.8, você também poderá usar a nova
@tailrec
anotação para obter informações sobre quais métodos são otimizados.
Essa anotação permite marcar métodos específicos que você espera que o compilador otimize.
Você receberá um aviso se eles não forem otimizados pelo compilador.- No Scala 2.7 ou anterior, você precisará confiar no teste manual ou na inspeção do bytecode para descobrir se um método foi otimizado.
Exemplo:
você pode adicionar uma
@tailrec
anotação para ter certeza de que suas alterações funcionaram.
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 funciona a partir do REPL (exemplo das dicas e truques do 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 = {
^
O compilador Scala otimizará automaticamente qualquer método verdadeiramente recursivo de cauda. Se você anotar um método que acredita ser recursivo no final com a @tailrec
anotação, o compilador irá avisá-lo se o método não for realmente recursivo no final. Isso torna a @tailrec
anotação uma boa ideia, tanto para garantir que um método seja otimizado no momento quanto para que permaneça otimizável à medida que é modificado.
Observe que Scala não considera um método recursivo na cauda se puder ser sobrescrito. Assim, o método deve ser privado, final, em um objeto (em oposição a uma classe ou característica), ou dentro de outro método a ser otimizado.
A anotação é scala.annotation.tailrec
. Ele aciona um erro do compilador se o método não puder ser otimizado para chamada final, o que acontece se:
Ele é colocado logo antes de def
em uma definição de método. Funciona no REPL.
Aqui importamos a anotação e tentamos marcar um método como @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! A última invocação é 1.+()
, não length()
! Vamos reformular o método:
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
Observe que length0
é automaticamente privado porque está definido no escopo de outro método.
override
anotação em Java - o código funciona sem ela, mas se você colocá-lo lá, ele avisará se você cometeu um erro.