@tailrec
注釈は、コンパイラーが末尾再帰関数を最適化することを保証します。宣言の前に置いただけですか? Scalaがスクリプトモードで使用されている場合にも機能しますか(たとえば、:load <file>
REPLの下)?
" Tail calls、@tailrec and trampolines "ブログ投稿から:
- Scala 2.8では、新しい
@tailrec
アノテーションを使用して、最適化されているメソッドに関する情報を取得することもできます。
この注釈により、コンパイラが最適化することを望む特定のメソッドをマークできます。
コンパイラーによって最適化されていない場合、警告が表示されます。- Scala 2.7以前では、メソッドが最適化されているかどうかを判断するには、手動テストまたはバイトコードの検査に依存する必要があります。
例:
@tailrec
アノテーションを追加して、変更が機能したことを確認できます。
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)
}
}
そして、REPL( 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 = {
^
Scalaコンパイラは、真の末尾再帰メソッドを自動的に最適化します。末尾再帰であると思われるメソッドに@tailrec
アノテーションを付けた場合、コンパイラは次の場合に警告しますメソッドは実際には末尾再帰ではありません。これにより、メソッドが現在最適化可能であり、変更されたときに最適化されたままであるようにするために、@tailrec
アノテーションが良いアイデアになります。
Scalaは、メソッドをオーバーライドできる場合、メソッドを末尾再帰とは見なしません。したがって、メソッドは、クラスまたはトレイトではなく、オブジェクトのprivate、finalである必要があります) 、または最適化される別のメソッド内。
注釈は_scala.annotation.tailrec
_です。メソッドがテールコール最適化できない場合、コンパイラエラーが発生します。これは次の場合に発生します。
メソッド定義のdef
の直前に配置されます。 REPLで機能します。
ここで注釈をインポートし、メソッドを_@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 {
^
_
おっとっと!最後の呼び出しは、1.+()
ではなくlength()
です!メソッドを再定式化しましょう:
_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
_
_length0
_は別のメソッドのスコープで定義されているため、自動的にプライベートであることに注意してください。