4

スタックオーバーフローには、 Scalaで末尾再帰が可能な条件を説明するさまざまな回答があります。制限と、末尾再帰を利用する方法と場所を理解しています。私が理解していない部分は、プライベートメソッドまたはfinalメソッドへの制限が存在する理由です。

Scalaコンパイラが実際に再帰関数をバイトコードレベルで非再帰関数に変換する方法については調査していませんが、次のような動作をすると仮定します。Foo再帰関数を持つクラスがありますmod

class Foo {
  def mod(value: Int, denom: Int): Int = {
    if(denom <= 0 || value <= 0) 0
    else if(0 <= value && value < denom) value
    else mod(value - denom, denom)
  }
}

これは基本的なモジュロ関数であり、Scalaコンパイラーが次のような疑似Java-Scalaに変換されると思います。

class Foo {
  def mod(value: Int, denom: Int): Int = {
    if(denom <= 0 || value <= 0) return 0
    while(value > denom) value -= denom
    return value
  }
}

(私はその翻訳を台無しにしたと信じることができますが、詳細は重要ではないと思います。)

だから今私がサブクラスだとしましょうFoo

class Bar extends Foo {
  def mod(value:Int, denom: Int): Int = 1
}

これが機能しなくなるのは何ですか?JVMにとが呼び出されたときFoo/Barに、使用する必要modのある関数の解決に問題があるのはなぜですか。modこれは、基本関数が非再帰的である状況と異なるのはなぜですか?

これが事実であると私が見ることができるいくつかの考えられる理由は次のとおりです。

  1. 何らかの理由で、Scalaコンパイラーの実装はこれを処理しません(その場合は十分に公平です。もしそうなら、これを変更する計画はありますか?)

  2. 関数内Fooはコンパイル中に変更されるため、実際にはオーバーライドするメソッドはありません。modmod-non-recursiveFoomod

4

2 に答える 2

8

答えたばかりですが、あなた自身の例を見てみましょう。そのクラスFooを定義し、JARファイルとして使用できるようにしたとします。

次に、そのJarファイルを取得し、Fooを次のように拡張します。

class Bar extends Foo {
  def mod(value:Int, denom: Int): Int = {
    Logger.log("Received mod with "+value+" % "+denom)
    super.mod(value, denom)
}

さて、Foomodが自分自身を呼び出すとき、私のオブジェクトはでBarはなく、であるため、FooではなくBarFooに行くことになっています(そしてそうします)。 mod

そしてそれが真実であるため、あなたが示したようにそれを最適化することはできません。

スーパークラスがそれ自体でメソッドを呼び出すときに、そのメソッドがオーバーライドされている場合、呼び出されるのはサブクラスのメソッドになるというのは、サブクラス化のコントラクトです。

メソッドをプライベートとして宣言するか、finalにするか、クラスにするか、メソッドの代わりに再帰関数を作成することで、サブクラスの実装に移動する必要がなくなる可能性があります。

于 2009-11-09T20:25:55.327 に答える
-1

IttayDは、今日この質問をしました。答えは、Fooの末尾再帰は、サブクラスでmodをオーバーライドできない場合にのみ最適化されるということです(クラスがfinalであるため、またはメソッドがfinalまたはprivateであるため)。

于 2009-11-09T19:23:25.677 に答える