2013-06-26 8 views
12

私は末尾の再帰的メソッドを持つために約@tailrecの注釈を使用して読んでいます。私はそれを説明する多くのリンクを見てきました。例えば、それは自己呼び出し関数のためにのみ機能し、オーバーライドされてはならない。@tailrecはどのように動作するのですか

どこでもcompiler optimizesが言及されている。しかし、コンパイラはそれを尾を再帰的にするためにどのような魔法/コンセプトをしますか?以下の簡単な関数、コンパイラは何をするのかについて:

@tailrec def fact(acc: Int, n: Int): Int = { 
    if (n <= 1) acc 
    else fact(n * acc, n - 1) 
} 
fact(1,10) 

私はそれはそれはそれを繰り返し呼び出し、最終的な値を返しループに変換するとはどういう意味しますか?ここで

+1

基本的には、scalaコンパイラはコードをwhileループと同様にバイトコードに変換します。おそらく 'var acc = 1;のようなことをしています。 var n = 10; start:if(n <= 1)は、accを返します。else {acc = n * acc; n = n-1; goto start} 'を実行します。すべてのテールコールを身体の開始点に機械的に置き換えることが可能でなければなりません。 – huynhjl

答えて

11

に記載されている:

var acc = 1 
    var n = 10 
start: 
    if (n <= 1) return acc 
    else { 
    acc = n * acc 
    n = n - 1 
    goto start 
    } 

私はちょうど持って起こった最近のビルドでとscalac -Xprint:allfactメソッドをコンパイルしてみましたコンパイラは何とかicodeファイルを発行しました。これは実際にテールコールを最適化する方法を実際に示しています。

// methods 
    def fact(acc: Int (INT), n: Int (INT)): Int { 
    locals: value acc, value n, value _$this 
    startBlock: 1 
    blocks: [1,2,3,4,5] 

    1: 
    2 JUMP 2 

    2: // huynhjl's comment: IF condition is here 
    3 LOAD_LOCAL(value n) 
    3 CONSTANT(1) 
    3 CJUMP (INT)LE ? 3 : 4 

    3: // huynhjl's comment: first branch of IF, will return acc 
    3 LOAD_LOCAL(value acc) 
    3 JUMP 5 

    5: 
    2 RETURN(INT) 

    4: // huynhjl's comment: else branch of IF, update acc and n and jump back 
    4 LOAD_LOCAL(value n) 
    4 LOAD_LOCAL(value acc) 
    4 CALL_PRIMITIVE(Arithmetic(MUL,INT)) 
    4 LOAD_LOCAL(value n) 
    4 CONSTANT(1) 
    4 CALL_PRIMITIVE(Arithmetic(SUB,INT)) 
    4 STORE_LOCAL(value n) 
    4 STORE_LOCAL(value acc) 
    4 JUMP 2 

    } 
8

を説明した紙にそこに任意のリンクは素敵なBlog post@tailrec

対象になっているだけで末尾呼び出しの最適化は、コンパイラによって実行することができない場合は、エラーが発行されることを保証します。スカラはデフォルトでテールコールの最適化を実行します。

用紙の条件が満たされた場合、フレームのスタックではなく最後のフレームを保持してループを実行することができます。プロセスがよりよく(ここでは、コードをrepasting)あなたの質問に私のコメントに加えてhere

+2

私はそれを見ました。それはそれを説明しません。彼は非自己救出メトードに使用できるトランポリンについて言及している。しかし、彼が与えた解決策はtail-recursiveではありません – Jatin

+1

@tailrecは警告ではなくコンパイルエラーを発行します。 –

+0

@VincenzoMaggio ok - "警告します"という意味の警告を意味しました。私はそれを編集します。 –