2010-01-10 29 views
7

Mam zestaw klas modeli i zestaw algorytmów, które można uruchomić na modelach. Nie wszystkie klasy modeli mogą wykonywać wszystkie algorytmy. Chcę, aby klasy modeli mogły zadeklarować jakie algorytmy mogą wykonać. Algorytmy, które może wykonać model, mogą zależeć od jego argumentów.scala: mixins w zależności od rodzaju argumentów

Przykład: Załóżmy, mam dwa algorytmy MCMC i znaczenie reprezentowane jako cechy:

trait MCMC extends Model { 
    def propose... 
} 

trait Importance extends Model { 
    def forward... 
} 

mam klasę modelu Normalny, która przyjmuje średnią argumentu, który sam model. Teraz, jeśli średnia implementuje MCMC, chcę, aby Normal implementował MCMC, i jeśli średnia implementuje Ważność, chcę, żeby Normalna implementowała Ważność.

mogę napisać: klasa normalna (znaczy: Wzorcowa) rozciąga model { // niektóre wspólne rzeczy idzie tu }

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 
} 

mogę utworzyć metody fabryki, upewnij się, że odpowiedni rodzaj Normal dostaje stworzone z podanym środkiem. Ale oczywiste pytanie brzmi: co jeśli implementuje MCMC i znaczenie? Potem chcę, żeby Normal także zaimplementował oba. Ale nie chcę tworzyć nowej klasy, która reimplements proponuje i przekazuje dalej. Gdyby NormalMCMC i NormalImportance nie przyjmowały argumentów, mógłbym zrobić z nich cechy i je połączyć. Ale chcę, żeby mieszanie było zależne od typu argumentu. Czy istnieje dobre rozwiązanie?

Odpowiedz

1

Wiele z twojego problemu wydaje się być, że NormalMCMC i NormalImportance przyjmują argumenty, ale, jak poprawnie sugerujesz, cechy nie mogą mieć konstruktorów.

Zamiast tego możesz pobrać parametry, które chcesz dostarczyć za pomocą konstruktora cech (jeśli coś takiego istnieje) i uczynić je abstrakcyjnymi członkami cechy.

Członkowie wtedy realizują się, gdy cecha jest skonstruowana.

Dane:

trait Foo { 
    val x : String //abstract 
} 

można używać go jako jedną z następujących czynności:

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

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

co daje powrotem równoważną funkcjonalność za pomocą konstruktorów cechy.

Zauważ, że jeśli typ Bar już non-streszczenie val x : String następnie można po prostu użyć

new Bar with Foo 

W niektórych sytuacjach może to również pomóc, aby x leniwy, co może daje większą elastyczność, jeśli kolejność inicjalizacji powinien stać się problemem.

7

Korzystanie self types pozwala oddzielić implementacje modela algorytm z dawałaby i wymieszać je w:

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

Dzięki Kevin, Mitch i Naftoli Gugenheim i Daniel Sobral na skalę użytkownikami listy, Mam dobrą odpowiedź. Dwie poprzednie odpowiedzi działają, ale prowadzą do ekspansyjnego powiększenia liczby cech, klas i konstruktorów. Jednak używanie implikacji i widoków pozwala uniknąć tego problemu.Kroki rozwiązania to:

1) Podaj Normalny parametr typu reprezentujący typ jego argumentu. 2) Zdefiniuj implicity, które przyjmują Normalny z właściwym rodzajem argumentu do tego, który implementuje odpowiedni algorytm. Na przykład makeImportance przyjmuje wartość Normal [Ważność] i tworzy wartość NormalImportance. 3) Implikom należy nadać typ związany. Powodem jest to, że bez powiązania z typem, jeśli spróbujesz przekazać wartość Normalną [T], aby wykonać Import, gdzie T jest podtypem ważności, to nie zadziała, ponieważ Normalna [T] nie jest podtypem Normalna [Ważność], ponieważ Normalna jest nie kowariantny. 4) Tego typu granice muszą być widoczne, aby implicity mogły się łączyć.

Oto pełne rozwiązanie:

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) 
} 
Powiązane problemy