3

私が理解している限り、Scala 線形化関数アルゴリズムは、結果リスト内の各特性の最後の出現以外をすべて削除する前に、右優先、深さ優先検索です。

このアルゴリズムには、私にはかなり恣意的に見える 2 つの側面があります。

  • クラスよりも特性が最初に調査され、その逆ではないのはなぜですか?
  • 特性が左から右ではなく、右から左にトラバースされるのはなぜですか?

これらの任意の規則は、他の規則と同じくらい良いですか? それとも、これらの設計上の決定の背後にある理論的根拠があるのでしょうか?

私の質問は、アルゴリズムの「水平」順序付けのみに関係することに注意してください。

4

1 に答える 1

9

これらの恣意的な慣習は、他の慣習と同じくらい良いものですか?

いいえ、これらは任意ではありません。正しいMethod Resolution Order (MRO)を提供する唯一の方法であるため、線形化はこの方法で行われます。

クラスよりも特性が最初に調査され、その逆ではないのはなぜですか?

これは、特定のクラス定義にのみ当てはまります。つまり、それが拡張するトレイトは、それが拡張するクラスの前に来ます。これは、スーパークラス/特性が右から左に探索されるという事実から導き出されたものです。

特性が左から右ではなく、右から左にトラバースされるのはなぜですか?

これは非常に単純な動機付けの例です。

type MyType = T1 with T2
// Creating new instance using alias
new MyType with T3
// Creating new instance directly
new T1 with T2 with T3

上記の例の両方のインスタンスが同じ線形化を持つことが期待されます。型エイリアスを使用する場合、追加の特性を右側に「スタック」します。したがって、右側の特性が MRO で最も優先され、線形化で最初に来ることが期待されます。

トレイトとクラスの線形化を説明するために私が思いついた簡単な例を次に示します。

class Base { override def toString = "Base" }
trait A { abstract override def toString = "A " + super.toString }
trait B { abstract override def toString = "B " + super.toString }
trait C { abstract override def toString = "C " + super.toString }
trait D { abstract override def toString = "D " + super.toString }
trait E { abstract override def toString = "E " + super.toString }
trait F { abstract override def toString = "F " + super.toString }
class X extends Base with A with B { override def toString = "X " + super.toString }
class Y extends X with C with D { override def toString = "Y " + super.toString }
class Z extends Y with E with F { override def toString = "Z " + super.toString }
new Z
// res0: Z = Z F E Y D C X B A Base

toString出力から、ここでの線形化が であることがわかりZ F E Y D C X B A Baseます。これは、指定された階層で期待するものとまったく同じです。たとえば、 trait を mix-in するZextendsから、の動作は の前に来ると予想されますが、その mix-inとの後に来ると予想されます。YCYCZEF

Programming in Scala, First Edition: Traits の第 12 章に関する簡単なリファレンスです。(これは最近変わっていないと思うので、初版はまだ正確なはずです。)

于 2013-11-06T09:17:10.823 に答える