2016-08-30 6 views
4

私はM. Odersky著によるスカラ座でのプログラミングを読んでいると、彼はなぜ機能テールは再帰的ではありませんか?

Functions like approximate, which call themselves as their last action, are called tail recursive.

だから、私はこの試みたことを述べている:

object Main extends App { 
    implicit val mc = new MyClass(8) 
    val ti = new TestImplct 
    ti.test 
} 

class TestImplct { 
    def test(implicit mc : MyClass): Unit = { 
    println(mc.i) 
    mc.i -= 1 
    if(mc.i < 0){ 
     throw new IllegalArgumentException 
    } 
    test 
    } 
} 

class MyClass(var i : Int) 

IDEONE DEMO

をしかし、それは次のスタックトレースを生成し

Exception in thread "main" java.lang.IllegalArgumentException 
    at TestImplct.test(Main.scala:13) 
    at TestImplct.test(Main.scala:15) 
    at TestImplct.test(Main.scala:15) 
    at TestImplct.test(Main.scala:15) 
    at TestImplct.test(Main.scala:15) 
    at TestImplct.test(Main.scala:15) 
    at TestImplct.test(Main.scala:15) 
    at TestImplct.test(Main.scala:15) 
    at TestImplct.test(Main.scala:15) 

つまり、ge再帰呼び出しごとに新しいスタックフレームをネーティングします。しかし、最後のアクションは、それ自身を呼び出すことです。何が間違っていて、それを末尾再帰的にするのですか?

なぜコンパイラはテールコールの最適化を行いませんか?

+0

MyClassの暗黙的なインスタンスが範囲内にありますか? – Samar

+0

@Samarはい、あります。デモを見て – stella

+0

私はあなたがクラスを定義したのを見ます。しかし、私はあなたがそれをインスタンス化した場所を見ていません。 – Samar

答えて

9

@tailrecアノテーションを使用してメソッドをマークできます。あなたがそれを行う場合は、コンパイルは失敗し、コンパイラは末尾再帰になるこの最適化できなかった理由を紹介します:あなたはメソッドfinalを作る場合に予想されるとして、実際に

Main.scala:12: error: could not optimize @tailrec annotated method test: it is neither private nor final so can be overridden

は、それが動作します。

+0

@ tailrecは本当にクールなものです。ありがとうございました! – stella

+1

興味深い。最終的にこのメソッドを最終的にTCOできるようにするのはなぜですか? – Samar

+0

@Samar + 1また、知りたいことがあります。 – stella

2

コードは正しく動作します。 mcオブジェクトの値はすべてのステップで減少します。最後のステップで例外が発生します。

あなたは、例えばBooleanなると、あなたの価値が< 0そうでなければ、trueを返すときfalseを返すために、あなたの関数の戻り値の型を変更することができます。

コンパイラによる再帰関数呼び出しをチェックするには、@tailrecアノテーションを使用することをお勧めします。

関連する問題