@tailrec
コンパイラが末尾再帰関数を最適化するようにするための注釈があると思います。宣言の前に置くだけですか?Scala がスクリプト モードで使用されている場合 (たとえば:load <file>
、REPL で使用している場合) にも機能しますか?
3 に答える
「テールコール、@tailrec、およびトランポリン」ブログ投稿から:
@tailrec
Scala 2.8 では、新しいアノテーションを使用して、最適化されたメソッドに関する情報を取得することもできます。
この注釈を使用すると、コンパイラが最適化することを望む特定のメソッドをマークできます。
コンパイラによって最適化されていない場合は、警告が表示されます。- 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 はそのメソッドを末尾再帰とは見なさないことに注意してください。したがって、メソッドはプライベート、ファイナル、オブジェクト (クラスまたはトレイトではなく) 上、または最適化される別のメソッド内のいずれかである必要があります。
注釈はscala.annotation.tailrec
. メソッドを末尾呼び出しで最適化できない場合、コンパイラ エラーが発生します。これは、次の場合に発生します。
- 再帰呼び出しが末尾位置にありません
- メソッドはオーバーライドされる可能性があります
- メソッドが final ではない (上記の特殊なケース)
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
別のメソッドのスコープで定義されているため、自動的にプライベートであることに注意してください。