0

私は言語を学ぶために Scala で小さなレンダリング エンジンに取り組んでいます。エンジンのアーキテクチャは特性に強く基づいているため、必要に応じてパイプラインの一部を追加および削除できます。特徴のいくつかは次のとおりです。

(更新: いくつかのタイプ情報を修正しました)

trait Composable { val parent:DisplayObject with Composite = null }
trait Composite extends Composable { val children:ArrayBuffer[DisplayObject with Composable] = ArrayBuffer() }
trait Position { var x = 0.0d; var y = 0.0d }
...

DisplayObjectこれらの特性を構成するために使用される空のクラスです。

私のパイプラインのステップの 1 つは、各オブジェクトの階層をフラット化することです。これでの私の最初のショットは次のとおりでした:(更新:本文を追加しました)

def flatten(root:DisplayObject with Composite) : ArrayBuffer[DisplayObject] =
{
    def traverse(composite:Composite, acc:ArrayBuffer[DisplayObject])
    {
      acc += composite
      for(composable <- composite.children)
      {
        composable match {
          case com:Composite => traverse(com, acc)
          case _ => acc += composable
        }
      }
    }

    val flat = new ArrayBuffer[DisplayObject]
    traverse(root, flat)
    flat
  }
}

これは問題ありませんが、この関数を呼び出すと、多くの型情報が失われます。

val root = new DisplayObject with Position with Composite
root.children += new DisplayObject with Composable with Position
val list = flatten(root)

のタイプlistは現在List[DisplayObject]です。位置情報が失われています。そこで、ジェネリックをミックスに追加することを考えました:(更新:追加された本体)

  def genericFlatten[T](root:T with Composite) : ArrayBuffer[T] =
  {
    def traverse(composite:T with Composite, acc:ArrayBuffer[T])
    {
      acc += composite
      for(composable <- composite.children)
      {
        composable match {
          case com:T with Composite => traverse(com, acc)
          case composable:T with Composable => acc += composable
        }
      }
    }

    val flat = new ArrayBuffer[T]
    traverse(root, flat)
    flat
  }

ただし、これを呼び出すと、この奇妙な結果が得られます。返されるリストのタイプは nowList[DisplayObject with Position with Composite]です。これは間違っています。これは、ツリーの一部の子 (リーフ) が Composite トレイトを持たないためです。タイプ T が に推論されることを期待していましたDisplayObject with Position。いいえ?

4

2 に答える 2

1

私は間違っているかもしれませんが、型推論が行うことについてある種の誤解を感じています。型を指定しないと、型推論はそれを理解しようとしますが、自分で定義した型を置き換えません。

最初のショットを撮りましょう。

def flatten(root:DisplayObject with Composite) : List[DisplayObject] 

ここでは、戻り値のタイプを設定します。このメソッドは常に aList[DisplayObject]を返し、推論するものはまったくありません。署名は完全に完了しています。

2 番目のショットを撮りましょう。

def flatten[T](root:T with Composite) : List[T] 

ここでも、型推論はありません。ジェネリック パラメータがあり、ジェネリック値はコンパイラによってチェックされます。このメソッドは、型推論がまったくない Java で記述できます。


children私があなたの答えを正しく解釈した場合、リスト内の要素を型を失うことなく平坦化したいと考えています。ただし、 Composite トレイトを見ると、次のようになります。

trait Composite extends Composable { 
        val children:ArrayBuffer[Composable] = new ArrayBuffer[Composable] 
}

ここには type を持つ がありval childrenます。ArrayBuffer[Composable]より具体的にはArrayBuffer[T]、ジェネリック パラメータを持つ type を持っていますT = Composable。これは、宣言で強制するコンパイル型であり、Scala や Java などの静的型付きプログラミング言語では、プログラムの実行中に変更することはできません。

これが問題を理解するための重要なポイントです。Java で考えてみてください。を持っているList<Object>場合は、整数の中に入れることができますが、それはあなたList<Object>List<Integer>. 子を追加するコードを 2 行に分割してみましょう。

val firstChildren:DisplayObject with Position = new DisplayObject with Position
root.children += firstChildren

firstChildrenここで、 val がスコープ外になるとすぐに、型情報が失われます。root.children を介してアクセスすると、それが であることはわかりませんがDisplayObject with PositionComposable. さらに、firstChildren は DisplayObject であり、


免責事項:

* Composable および Composite クラスには循環参照があるため、あなたがしようとしていることは簡単ではありません。私はいくつかの単純な作業コードを提供するためにそれを壊してきましたが、型システムを十分に習得するには、Scala である程度の経験が必要になることを警告する必要があります。*

親の子型に関する情報と子の親に関する情報を保持する必要があります。したがって、2 つの型パラメーターが必要です。

  trait Composable[K,T<:Composite[K,T]] {
    val parent:T
  }

  trait Composite[K,T<:Composite[K,T]] extends Composable[K,T] {
    val children:ArrayBuffer[K] = new ArrayBuffer[K]
  }

  trait Position { val x = 0.0d; val y = 0.0d }

  class DisplayObject

  def flatten[K,T<:Composite[K,T]](root:DisplayObject with Composite[K,T]) : List[K] =
  {
    root.children.toList
  }
  class ComposableDisplayObjectWithPosition extends DisplayObject with Position with        Composite[DisplayObject with Position,ComposableDisplayObjectWithPosition]{
      // dangerous
      val parent = this
    }

  def main(args:Array[String]){

    val root = new ComposableDisplayObjectWithPosition
    root.children += new DisplayObject with Position
    val list:List[DisplayObject with Position] = flatten(root)
    println(list)
  }
于 2012-12-11T08:48:24.810 に答える
1

免責事項: 私は Scala の専門家ではありません。以下の議論は一般的なものです (しゃれを許してください)。

シグネチャfunc(param:T with X)は、 の実パラメータが の場合funcおよびAが必要であることを意味します。これらの要件は完全に別個のものです。A<:T A<:X

without型システムにはありません。が一般的である場合T、 であると推論されませんA without X。そのようなことはありません。を満たすための唯一の最も簡単で最も有用な方法であるため、 でTあると推測されます。別途チェックされます。AA<:TA<:X

に渡すDisplayObject with Position with Compositeflatten、 が返されDisplayObject with Position with Compositeます。を渡すFoo with Bar with Compositeと、それも返されます。Compositeは存在しないので、を引くことはできませんwithout。なぜ誰もがしたいのかわからない。余分な型情報が害を及ぼす状況は想像できません。

于 2012-12-11T11:07:28.330 に答える