2010-01-10 25 views
7

Tôi có một tập hợp các lớp mô hình và một bộ thuật toán có thể chạy trên các mô hình. Không phải tất cả các lớp mô hình đều có thể thực hiện tất cả các thuật toán. Tôi muốn các lớp mô hình có thể khai báo những thuật toán nào họ có thể thực hiện. Các thuật toán mà một mô hình có thể thực hiện có thể phụ thuộc vào các đối số của nó.scala: mixins tùy thuộc vào loại đối số

Ví dụ: Giả sử tôi có hai thuật toán, MCMC, và tầm quan trọng, thể hiện dưới dạng những đặc điểm:

trait MCMC extends Model { 
    def propose... 
} 

trait Importance extends Model { 
    def forward... 
} 

Tôi có một lớp mô hình bình thường, trong đó có một cuộc tranh luận có ý nghĩa, mà là chính nó một mô hình. Bây giờ, nếu có nghĩa là thực hiện MCMC, tôi muốn Normal để thực hiện MCMC, và nếu có nghĩa là thực hiện quan trọng, tôi muốn Normal để thực hiện tầm quan trọng.

tôi có thể viết: lớp bình thường (nghĩa là: Model) mở rộng mẫu {// một số công cụ phổ biến goes here }

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 
} 

tôi có thể tạo ra các phương pháp nhà máy đảm bảo đúng loại bình thường được được tạo với một giá trị nhất định. Nhưng câu hỏi rõ ràng là, điều gì sẽ xảy ra nếu trung bình thực hiện cả MCMC và Tầm quan trọng? Sau đó, tôi muốn bình thường để thực hiện cả hai người trong số họ quá. Nhưng tôi không muốn tạo ra một lớp học mới để tái hiện đề xuất và tiến lên. Nếu NormalMCMC và NormalImportance không đưa ra các đối số, tôi có thể tạo ra các đặc điểm đó và trộn chúng vào. Nhưng ở đây tôi muốn việc trộn vào phụ thuộc vào loại đối số. Có một giải pháp tốt?

Trả lời

1

Phần lớn vấn đề của bạn dường như là NormalMCMCNormalImportance lấy các đối số nhưng, như bạn ngụ ý chính xác, các đặc điểm không thể có các hàm tạo. Thay vào đó, bạn có thể lấy các tham số mà bạn muốn cung cấp thông qua một hàm tạo đặc điểm (nếu có một thứ như vậy) và làm cho chúng trở thành thành viên trừu tượng của đặc điểm này.

Các thành viên sau đó được nhận ra khi đặc điểm được tạo.

Given:

trait Foo { 
    val x : String //abstract 
} 

bạn có thể sử dụng nó như một trong hai điều sau đây:

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

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

Mà cho bạn trở lại các chức năng tương đương với sử dụng constructor Trait.

Lưu ý rằng nếu loại Bar đã có một tổ chức phi trừu tượng val x : String sau đó bạn có thể chỉ cần sử dụng

new Bar with Foo 

Trong một số tình huống nó cũng có thể giúp đỡ để làm cho x lười biếng, mà có thể giúp bạn linh hoạt hơn nếu để khởi tạo nên trở thành một vấn đề.

7

Sử dụng self types cho phép bạn tách biệt việc triển khai Model-Thuật toán từ sự khởi tạo và trộn chúng trong:

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

Nhờ Kevin, Mitch, và Naftoli Gugenheim và Daniel Sobral trên phạm vi người sử dụng danh sách gửi thư, Tôi có một câu trả lời tốt. Hai câu trả lời trước đó có tác dụng, nhưng dẫn đến một sự bùng nổ theo hàm mũ về số lượng các đặc điểm, các lớp và các nhà xây dựng. Tuy nhiên, việc sử dụng implicits và view bounds tránh được vấn đề này.Các bước của giải pháp là:

1) Cung cấp thông số loại bình thường đại diện cho loại đối số của nó. 2) Xác định các implicits lấy một Normal với kiểu đối số phù hợp với một đối số thực hiện thuật toán thích hợp. Ví dụ, makeImportance lấy một [Tầm quan trọng bình thường] và tạo ra một NormalImportance. 3) Những lời buộc tội cần phải được đưa ra một loại ràng buộc. Lý do là nếu không có kiểu bị ràng buộc, nếu bạn cố chuyển một Normal [T] tới makeImportance trong đó T là một kiểu con của Tầm quan trọng, nó sẽ không hoạt động vì Normal [T] không phải là một kiểu con của Normal [Quan trọng] vì Normal là không covariant. 4) Các giới hạn kiểu này cần được xem giới hạn để cho phép sự liên quan đến chuỗi.

Đây là giải pháp đầy đủ:

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) 
} 
Các vấn đề liên quan