2012-05-13 25 views
6

Tôi có lớp sau generic Interval (vui lòng xây dựng cho tôi bằng cách sử dụng soc):Scala ngầm Numeric [T] trong đối tượng đồng

case class Interval[T](from: T, to: T)(implicit num: Numeric[T]) { 
    import num.mkNumericOps // allows us to write from.toDouble and to.toDouble 
    def mid: Double = (from.toDouble + to.toDouble)/2.0 
} 

trường hợp sử dụng điển hình: Interval [đúp] hoặc Interval [Int]. Để thêm nhị phân đoànngã tư khai thác Tôi đi theo một mô hình tương tự với (implicit num: Numeric[T]) trong đối tượng đồng:

object Interval { 

    def union[T](interval1: Interval[T], interval2: Interval[T])(implicit num: Numeric[T]) = { 
    import num.mkOrderingOps // allows interval1.from min 
    Interval[T](interval1.from min interval2.from, interval1.to max interval2.to) 
    } 

    def intersect[T](interval1: Interval[T], interval2: Interval[T])(implicit num: Numeric[T]) = { 
    import num.mkOrderingOps 
    Interval[T](interval1.from max interval2.from, interval1.to min interval2.to) 
    } 

} 

Đó là soạn sẵn xấu xí để sao chép các (implicit num: Numeric[T])import num.mkOrderingOps bên trong cả hai phương pháp. Có cách nào để làm điều này chỉ một lần, ở cấp độ của đối tượng Interval chính nó?

Trả lời

8

Có.

Đầu tiên bằng cách nhập. Bạn có thể nhập Ordering.Implicits._ trong phạm vi Khoảng thời gian thay thế.

object Interval { 
    import Ordering.Implicits._ 

    def union[T](....)(implicit num: Numeric[T]) = { 
    // do not import num.mkOrderingOps 
    ... 
    } 
    ... 
} 

Với những điều đó, khi tìm thấy thao tác đặt hàng, nó sẽ tìm Thứ tự ngầm định (Numeric is a Ordering) trong phạm vi hoạt động xảy ra. Và sẽ xảy ra một tiềm ẩn thích hợp trong phạm vi trong mỗi thói quen của bạn. Nếu bạn cũng cần các phép toán số học, hãy nhập Numeric.Implicits._

Bây giờ với đối số ngầm định.Có một lối tắt trong ngôn ngữ cho điều đó, được gọi là ngữ cảnh bị ràng buộc: bạn có thể viết def f[T: X](args) thay vì def f[T](args)(implicit someName: X[T])

Sự khác biệt là bạn không có tên cho ngầm với ngữ cảnh bị ràng buộc (bạn có thể sử dụng implictly [T] nhưng sau đó này là hầu như không ngắn hơn. may mắn thay, bạn không cần một tên nữa với Ordering.Implicits._ nhập khẩu

Vì vậy

object Interval { 
    import Ordering.Implicits._ 
    // also import Numeric.Implicits._ if you need +,-,*,/ ... 
    def union[T: Numeric] ... 
    def intersection[T: Numeric] ... 
} 
+0

Cảm ơn bạn. Đẹp ngắn gọn. Tôi chưa bao giờ thấy bối cảnh ràng buộc này. Người ta học những thứ như thế ở đâu? Tôi đọc một cuốn sách về Scala, nhưng tôi không nhớ lại giới hạn ngữ cảnh. –

+0

Xem ví dụ [ngữ cảnh là gì "trong Scala?] (Http://stackoverflow.com/questions/2982276/what-is-a-context-bound-in-scala). – Jesper

5

Sử dụng loại lớp Numeric trong đối tượng Interval có thông số loại T phải được đặt ở đâu đó trong phạm vi bao quanh của chúng. Interval, là một giá trị không đổi duy nhất, không thể cung cấp liên kết đó.

Một giải pháp cho vấn đề cụ thể này sẽ được di chuyển các định nghĩa của unionintersect của bạn hoạt động vào Intervallớp phương pháp dụ như bình thường, trong trường hợp đó họ sẽ chia sẻ những ràng buộc của T và liên Numeric dụ với phần còn lại của lớp,

case class Interval[T : Numeric](from: T, to: T) { 
    import Numeric.Implicits._ 
    import Ordering.Implicits._ 

    def mid: Double = (from.toDouble + to.toDouble)/2.0 
    def union(interval2: Interval[T]) = 
    Interval(this.from min interval2.from, this.to max interval2.to) 
    def intersect(interval2: Interval[T]) = 
    Interval(this.from max interval2.from, this.to min interval2.to) 
} 

Tuy nhiên, nếu bạn muốn giữ các định nghĩa của các hoạt động này tách biệt với Interval lớp, một cách tiếp cận để giảm lượng soạn sẵn tiềm ẩn mà bạn cần phải đuổi throug h API của bạn là để xác định các lớp loại cấp cao hơn của riêng bạn về số [T]. Ví dụ,

// Type class supplying union and intersection operations for values 
// of type Interval[T] 
class IntervalOps[T : Numeric] { 
    import Ordering.Implicits._ 

    def union(interval1: Interval[T], interval2: Interval[T]) = 
    Interval[T](interval1.from min interval2.from, interval1.to max interval2.to) 

    def intersect(interval1: Interval[T], interval2: Interval[T]) = 
    Interval[T](interval1.from max interval2.from, interval1.to min interval2.to) 
} 

implicit def mkIntervalOps[T : Numeric] = new IntervalOps[T] 

mà sử dụng sẽ như thế nào,

def use[T](i1 : Interval[T], i2 : Interval[T])(implicit ops : IntervalOps[T]) = { 
    import ops._ 
    val i3 = union(i1, i2) 
    val i4 = intersect(i1, i2) 
    (i3, i4) 
} 

Một lựa chọn thứ ba kết hợp hai, sử dụng một định nghĩa tiềm ẩn để làm phong phú thêm lớp ban đầu với các phương pháp bổ sung,

class IntervalOps[T : Numeric](interval1 : Interval[T]) { 
    import Ordering.Implicits._ 

    def union(interval2: Interval[T]) = 
    Interval[T](interval1.from min interval2.from, interval1.to max interval2.to) 

    def intersect(interval2: Interval[T]) = 
    Interval[T](interval1.from max interval2.from, interval1.to min interval2.to) 
} 

implicit def enrichInterval[T : Numeric](interval1 : Interval[T]) = 
    new IntervalOps[T](interval1) 

type Ops[T] = Interval[T] => IntervalOps[T] 

Sau đó sử dụng,

def use[T](i1 : Interval[T], i2 : Interval[T])(implicit ops : Ops[T]) = { 
    val i3 = i1 union i2 
    val i4 = i1 intersect i2 
    (i3, i4) 
} 
+0

Ngoài ra, đối tượng compainon 'Interval' là đẹp để đặt các chuyển đổi ngầm, sau đó, trong ví dụ cuối cùng với phương thức 'use' bạn chỉ cần pro vide 'Numeric' context bounds' sử dụng [T: Numeric] (i1: Interval [T], i2: Interval [T]) ' – 4e6

+0

Cảm ơn Miles. Rất nhiều grist cho nhà máy của tôi! –

Các vấn đề liên quan