2010-01-10 7 views
7

Ich habe eine Reihe von Klassen von Modellen und eine Reihe von Algorithmen, die auf den Modellen ausgeführt werden können. Nicht alle Klassen von Modellen können alle Algorithmen ausführen. Ich möchte, dass Modellklassen deklarieren können, welche Algorithmen sie ausführen können. Die Algorithmen, die ein Modell ausführen kann, können von seinen Argumenten abhängen.scala: mixins je nach Art der Argumente

Beispiel: Sagen wir, ich habe zwei Algorithmen, MCMC und Bedeutung, als Merkmale dargestellt:

trait MCMC extends Model { 
    def propose... 
} 

trait Importance extends Model { 
    def forward... 
} 

Ich habe eine Modellklasse Normal, die eine mittlere Argument, die selbst ist ein Modell. Nun, wenn der Durchschnitt MCMC implementiert, möchte ich, dass Normal MCMC implementiert, und wenn der Mittelwert Wichtigkeit implementiert, möchte ich, dass Normal die Wichtigkeit implementiert.

ich schreiben kann: Klasse Normale (Mittelwert: Model) erstreckt Modell { // einige gemeinsame Sachen geht hier }

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 
} 

I Factory-Methoden erstellen können, die die richtige Art von normal stellen Sie sicher, bekommt mit einem bestimmten Mittel erstellt. Aber die offensichtliche Frage ist, was, wenn Mittelwert sowohl MCMC als auch Bedeutung implementiert? Dann möchte ich, dass Normal beide auch implementiert. Aber ich möchte keine neue Klasse erstellen, die Reimplements vorschlagen und weiterleiten. Wenn NormalMCMC und NormalImportance keine Argumente hätten, könnte ich sie zu Merkmalen machen und sie mischen. Aber hier möchte ich, dass die Mischung von der Art des Arguments abhängt. Gibt es eine gute Lösung?

Antwort

1

Ein großer Teil Ihres Problem scheint zu sein, dass NormalMCMC und NormalImportance Argumente nehmen, aber, wie Sie richtig bedeuten, können Züge nicht Konstrukteure haben.

Stattdessen können Sie die Parameter, die Sie liefern möchten, über einen Merkmalskonstruktor (falls es so etwas gibt) nehmen und sie zu abstrakten Merkmalen des Merkmals machen.

Die Mitglieder werden dann erkannt, wenn das Merkmal konstruiert ist.

Gegeben:

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

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

Welche Sie gleichwertige Funktionalität mit Trait Bauer zurück gibt:

trait Foo { 
    val x : String //abstract 
} 

Sie es als eine der folgenden verwenden können.

Beachten Sie, dass, wenn der Typ Bar bereits eine nicht-abstrakte val x : String dann hat man einfach

new Bar with Foo 

In einigen Szenarien kann es auch, x faul machen helfen, wo Sie mehr Flexibilität, wenn Initialisierungsreihenfolge gibt sollte ein Problem werden.

7

Mit self types können Sie die Modell-Algorithmus-Implementierungen von den instantiations trennen und mischen sie in:

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 
4

Dank Kevin, Mitch und Naftoli Gugenheim und Daniel Sobral auf die Scale-users Mailingliste, Ich habe eine gute Antwort. Die beiden vorherigen Antworten funktionieren, führen jedoch zu einem exponentiellen Anstieg der Anzahl der Merkmale, Klassen und Konstruktoren. Die Verwendung von Implicits und View-Grenzen vermeidet dieses Problem jedoch.Die Schritte der Lösung sind:

1) Geben Sie Normal einen Typparameter, der den Typ des Arguments darstellt. 2) Definieren Sie Implicits, die eine Normale mit dem richtigen Argumenttyp zu einem Typ machen, der den entsprechenden Algorithmus implementiert. Zum Beispiel nimmt makeImportance eine normale [Wichtigkeit] und erzeugt eine normale Bedeutung. 3) Den Implicits muss eine Typgrenze zugewiesen werden. Der Grund dafür ist, dass ohne Typgebundene, wenn Sie versuchen, eine Normale [T] an makeImportance zu übergeben, wobei T ein Subtyp von Wichtigkeit ist, dies nicht funktioniert, weil Normal [T] kein Untertyp von Normal [Wichtigkeit] ist, weil Normal ist nicht kovariant. 4) Diese Typgrenzen müssen Sichtgrenzen sein, damit die Implikate ketten können.

Hier ist die vollständige Lösung:

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) 
}