2012-03-15 10 views
2

私が知っている関数はテール再帰的です。しかし、私が定義する方法のために、コンパイラは、非テール位置で再帰呼び出しを持つ関数について不満を抱いています。これが関数です。このテール再帰的なスカラ関数に注釈を付ける方法

@tailrec 
def travel: (Int, List[Char]) => Int = { 
    case (n,  Nil) => n 
    case (n, '~' :: sls) => travel(0, sls) 
    case (n, '^' :: sls) => travel(max(n-1,0), sls) 
    case (n, '>' :: sls) => travel(n+1, sls) 
    case (_, s :: sls) => throw new IllegalArgumentException("Illegal selector '" + s + "'") 
} 

私はこのようにそれを書けば、それが正常に動作します

error: could not optimize @tailrec annotated method travel: it contains a recursive call not in tail position 
def travel: (Int, List[Char]) => Int = { 

を取得します。

@tailrec 
def travel(n:Int, l:List[Char]): Int = (n,l) match { 
    case (n,  Nil) => n 
    case (n, '~' :: sls) => travel(0, sls) 
    case (n, '^' :: sls) => travel(max(n-1,0), sls) 
    case (n, '>' :: sls) => travel(n+1, sls) 
    case (_, s :: sls) => throw new IllegalArgumentException("Illegal selector '" + s + "'") 
} 

私はそれがdef: (Input) => Output = {}型宣言スタイルと関係があると思います。ネストされたマッチやタプルのマッチを書くよりもきれいに見えるので、私はそれを使います。

+0

私は「@ tailrec」アノテーションを無名関数に追加できますか?* – paradigmatic

+0

@paradigmatic - それは別の質問ですが、良いものです。アノテーションではありませんが、無名関数が末尾再帰呼び出しを行う方法です。 (独自の 'apply'メソッドを見ることはできません)。 –

答えて

7

2つは同じではありません。最初のケースでは、メソッドが関数を生成し、メソッドを再度呼び出します(関数などを生成します)。つまり、travelを最初のケースで呼び出すたびに、Function1[(Int, List[Char]), Int]という新しいインスタンスが作成されます。意外にも、これはジャンプ命令に変換することはできません。 (理論上は可能ですが、分析は非常に複雑です。すべてのオブジェクト作成を取り消す必要があります)。

2番目のケースでは、それ自体を呼び出すメソッドであり、ジャンプ。

関連する問題