2010-01-10 51 views
7

我有一組模型和一組可以在模型上運行的算法。並非所有類型的模型都可以執行所有算法。我希望模型類能夠聲明他們可以執行的算法。模型可以執行的算法可能取決於其參數。scala:mixins取決於參數的類型

示例:假設我有兩種算法,MCMC以及重要性,表示爲性狀:

trait MCMC extends Model { 
    def propose... 
} 

trait Importance extends Model { 
    def forward... 
} 

我有一個模型類普通,這需要的平均參數,它本身就是一個模型。現在,如果意思是實現MCMC,我希望Normal能夠實現MCMC,如果mean意味着實現了重要性,我希望Normal能夠實現重要性。

我可以這樣寫: 分類標準(平均模式)擴展模式{// 一些常用的東西放在這裏 }

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 
} 

我可以創建工廠方法確保正確的那種普通的獲取用給定的意思創造。但顯而易見的問題是,如果意味着實現MCMC和重要性呢?然後我希望Normal也能實現它們。但我不想創建一個重新實現並提出的新類。如果NormalMCMC和NormalImportance不需要參數,我可以使它們成爲特徵並將它們混合在一起。但是在這裏,我想要根據參數的類型進行混合。有沒有一個好的解決方案?

回答

1

你的問題很多似乎是NormalMCMCNormalImportance需要參數,但正如你正確暗示的,特徵不能有構造函數。

相反,您可以通過特質構造函數(如果存在這種情況)獲取想要提供的參數,並使它們成爲特徵的抽象成員。

當構建特質時,成員便會意識到。

考慮:

trait Foo { 
    val x : String //abstract 
} 

,你可以使用它作爲下面的任一:

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

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

,讓你回來使用特質構造的等效功能。

需要注意的是,如果該類型Bar已經有一個非抽象val x : String那麼你可以簡單地使用

new Bar with Foo 

在某些情況下它也可以有助於使x懶,它可以給你,如果初始化爲了更大的靈活性應該成爲一個問題。

7

使用self types允許您將模型的算法實現從實例中分離,並將它們在混合:

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

由於上規模用戶郵件列表凱文,米奇,和Naftoli古根海姆和丹尼爾索布拉爾,我有一個很好的答案。前面的兩個答案有效,但導致特徵,類和構造函數的數量急劇增加。但是,使用蘊涵和視圖邊界可以避免這個問題。解決方案的步驟如下:

1)給普通一個類型參數,表示它的參數類型。 2)定義含有正確類型參數的普通函數,以實現適當的算法。例如,makeImportance採用Normal [重要性]並生成一個NormalImportance。 3)蘊涵需要給定一個類型綁定。原因是沒有綁定類型,如果嘗試傳遞一個Normal [T]來創建重要性,其中T是重要性的子類型,它將不起作用,因爲Normal [T]不是Normal [重要性]的子類型,因爲Normal是不是協變的。 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) 
}