7

モデルのクラスのセットと、モデルで実行できるアルゴリズムのセットがあります。モデルのすべてのクラスがすべてのアルゴリズムを実行できるわけではありません。モデル クラスで、実行できるアルゴリズムを宣言できるようにしたいと考えています。モデルが実行できるアルゴリズムは、その引数に依存する場合があります。

例: 特性として表される 2 つのアルゴリズム MCMC と Importance があるとします。

trait MCMC extends Model {
  def propose...
}

trait Importance extends Model {
  def forward...
}

モデル クラス Normal があります。これは、それ自体が Model である mean 引数を取ります。ここで、mean が MCMC を実装する場合、Normal に MCMC を実装する必要があり、mean が Importance を実装する場合、Normal に Importance を実装する必要があります。

私は次のように書くことができます:

class NormalMCMC(mean: MCMC) extends Normal(mean) with MCMC {
  def propose...implementation goes here
}

class NormalImportance(mean: Importance) extends Normal(mean) with Importance {
  def forward...implementation goes here
}

適切な種類の法線が特定の手段で作成されるようにするファクトリ メソッドを作成できます。しかし明らかな疑問は、もし mean が MCMC と Importance の両方を実装しているとしたら? 次に、Normal にも両方を実装してもらいたいと思います。しかし、提案と転送を再実装する新しいクラスを作成したくありません。NormalMCMC と NormalImportance が引数を取らない場合は、それらをトレイトにして混合することもできます。しかし、ここでは、混合を引数の型に依存させたいと考えています。良い解決策はありますか?

4

3 に答える 3

7

自己型を使用すると、モデルアルゴリズムの実装をインスタンス化から分離し、それらを次のように組み合わせることができます。

trait Model
trait Result
trait MCMC extends Model {
  def propose: Result
}
trait Importance extends Model {
  def forward: Result
}

class Normal(val model: Model) extends Model

trait NormalMCMCImpl extends MCMC {
  self: Normal =>
  def propose: Result = { //... impl
    val x = self.model // lookie here... I can use vals from Normal
  }
}
trait NormalImportanceImpl extends Importance {
  self: Normal =>
  def forward: Result = { // ... impl
      ...
  }
}

class NormalMCMC(mean: Model) extends Normal(mean)
                              with NormalMCMCImpl

class NormalImportance(mean: Model) extends Normal(mean)
                                    with NormalImportanceImpl

class NormalImportanceMCMC(mean: Model) extends Normal(mean)
                                        with NormalMCMCImpl
                                        with NormalImportanceImpl
于 2010-01-10T21:32:33.637 に答える
4

scale-users メーリング リストの Kevin、Mitch、Naftoli Gugenheim、Daniel Sobral のおかげで、良い答えが得られました。前の 2 つの回答は機能しますが、トレイト、クラス、およびコンストラクターの数が指数関数的に増加します。ただし、暗黙的およびビュー境界を使用すると、この問題を回避できます。ソリューションの手順は次のとおりです。

1) Normal に引数の型を表す型パラメータを与えます。2) 適切なアルゴリズムを実装するものに対して、正しいタイプの引数を持つ法線を取る暗黙を定義します。たとえば、makeImportance は Normal[Importance] を受け取り、NormalImportance を生成します。3) Implicit には、タイプ バウンドを指定する必要があります。その理由は、型がバインドされていない場合、T が Importance のサブタイプである makeImportance に Normal[T] を渡そうとすると、Normal[T] が Normal[Importance] のサブタイプではないため、正常に機能しないためです。共変ではありません。4) これらの型の境界は、暗黙のチェーンを許可するビューの境界である必要があります。

完全なソリューションは次のとおりです。

class Model

trait Importance extends Model {
  def forward: Int
}

trait MCMC extends Model {
  def propose: String
}

class Normal[T <% Model](val arg: T) extends Model

class NormalImportance(arg: Importance) extends Normal(arg) with Importance {
  def forward = arg.forward + 1
}

class NormalMCMC(arg: MCMC) extends Normal(arg) with MCMC {
  def propose = arg.propose + "N"
}

object Normal {
  def apply[T <% Model](a: T) = new Normal[T](a)
}

object Importance {
  implicit def makeImportance[T <% Importance](n: Normal[T]): Importance = 
    new NormalImportance(n.arg)
}

object MCMC {
  implicit def makeMCMC[T <% MCMC](n: Normal[T]): MCMC = new NormalMCMC(n.arg)
}

object Uniform extends Model with Importance with MCMC {
  def forward = 4
  def propose = "Uniform"
}

def main(args: Array[String]) {
  val n = Normal(Normal(Uniform))
  println(n.forward) 
  println(n.propose)
}
于 2010-01-12T18:04:01.250 に答える
1

あなたの問題の多くはそれであり、引数NormalMCMCNormalImportance取るようですが、あなたが正しく暗示しているように、特性はコンストラクターを持つことができません。

代わりに、特性コンストラクター (存在する場合) を介して提供したいパラメーターを取得し、それらを特性の抽象メンバーにすることができます。

メンバーは、特性が構築されるときに実現されます。

与えられた:

trait Foo {
  val x : String //abstract
}

次のいずれかとして使用できます。

new Bar with Foo { val x = "Hello World" }

new Bar { val x = "Hello World" } with Foo

これにより、Trait コンストラクターを使用する場合と同等の機能が得られます。

Barタイプにすでに非抽象型がある場合は、val x : String単に使用できることに注意してください

new Bar with Foo

x一部のシナリオでは、 lazy にすることも役立ちます。これにより、初期化の順序が問題になる場合に、より柔軟に対応できます。

于 2010-01-10T17:23:58.887 に答える