Je pense qu'il y a @tailrec
annotation pour garantir que le compilateur optimisera une fonction récursive de queue. Le mettez-vous juste devant la déclaration? Cela fonctionne-t-il également si Scala est utilisé en mode script (par exemple en utilisant :load <file>
sous REPL)?
Extrait du blog " Tail calls, @tailrec and trampolines ":
- Dans Scala 2.8, vous pourrez également utiliser le nouveau
@tailrec
annotation pour obtenir des informations sur les méthodes optimisées.
Cette annotation vous permet de marquer des méthodes spécifiques que vous espérez que le compilateur optimisera.
Vous obtiendrez alors un avertissement s'ils ne sont pas optimisés par le compilateur.- Dans Scala 2.7 ou version antérieure, vous devrez vous fier à des tests manuels ou à l'inspection du bytecode pour déterminer si une méthode a été optimisée.
Exemple:
vous pouvez ajouter un
@tailrec
annotation pour vous assurer que vos modifications ont fonctionné.
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)
}
}
Et cela fonctionne à partir du REPL (exemple du Scala REPL trucs et astuces ):
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 = {
^
Le compilateur Scala optimisera automatiquement toute méthode véritablement récursive de queue. Si vous annotez une méthode que vous pensez être récursive de queue avec le @tailrec
annotation, le compilateur vous avertira si la méthode n'est en fait pas récursive. Cela rend le @tailrec
l'annotation est une bonne idée, à la fois pour garantir qu'une méthode est actuellement optimisable et qu'elle reste optimisable au fur et à mesure de sa modification.
Notez que Scala ne considère pas qu'une méthode est récursive si elle peut être remplacée. Ainsi, la méthode doit être soit privée, finale, sur un objet (par opposition à une classe ou un trait) ou à l'intérieur d'une autre méthode à optimiser.
L'annotation est scala.annotation.tailrec
. Il déclenche une erreur de compilation si la méthode ne peut pas être optimisée pour les appels de queue, ce qui se produit si:
Il est placé juste avant le def
dans une définition de méthode. Cela fonctionne dans le REPL.
Ici, nous importons l'annotation et essayons de marquer une méthode comme @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 {
^
Oops! La dernière invocation est 1.+()
, pas length()
! Reformulons la méthode:
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
Notez que length0
Est automatiquement privé car il est défini dans le cadre d'une autre méthode.