2011-02-09 38 views
73

Tôi cố gắng để tránh các cấu trúc như thế này:ternary điều hành tương tự như:?

val result = this.getClass.getSimpleName 
if (result.endsWith("$")) result.init else result 

Ok, trong ví dụ này thenelse chi nhánh là đơn giản, nhưng bạn có thể những hình ảnh phức tạp. tôi xây dựng như sau:

object TernaryOp { 
    class Ternary[T](t: T) { 
    def is[R](bte: BranchThenElse[T,R]) = if (bte.branch(t)) bte.then(t) else bte.elze(t) 
    } 
    class Branch[T](branch: T => Boolean) { 
    def ?[R] (then: T => R) = new BranchThen(branch,then) 
    } 
    class BranchThen[T,R](val branch: T => Boolean, val then: T => R) 
    class Elze[T,R](elze: T => R) { 
    def :: (bt: BranchThen[T,R]) = new BranchThenElse(bt.branch,bt.then,elze) 
    } 
    class BranchThenElse[T,R](val branch: T => Boolean, val then: T => R, val elze: T => R) 
    implicit def any2Ternary[T](t: T) = new Ternary(t) 
    implicit def fct2Branch[T](branch: T => Boolean) = new Branch(branch) 
    implicit def fct2Elze[T,R](elze: T => R) = new Elze(elze) 
} 

Defined đó, tôi có thể thay thế các ví dụ đơn giản trên với:

this.getClass.getSimpleName is {s: String => s.endsWith("$")} ? {s: String => s.init} :: {s: String => s} 

Nhưng làm thế nào tôi có thể thoát khỏi những s: String =>? Tôi muốn một cái gì đó như thế:

this.getClass.getSimpleName is {_.endsWith("$")} ? {_.init} :: {identity} 

Tôi đoán trình biên dịch cần thêm công cụ để suy ra các loại.

+0

Kể từ khi tôi đã không thực sự có điều này trong câu trả lời của tôi - lý do bạn' gặp rắc rối là suy luận kiểu hoạt động tốt nhất từ ​​trái sang phải, nhưng bạn ràng buộc các thẻ của bạn với nhau từ phải sang trái vì ưu tiên toán tử. Nếu bạn làm cho tất cả các từ tuyên bố của bạn (với cùng một ưu tiên) và thay đổi cách mọi thứ nhóm lại với nhau, bạn sẽ nhận được suy luận mà bạn muốn. (Tức là bạn sẽ có các lớp 'HasIs',' IsWithCondition', 'ConditionAndTrueCase' sẽ xây dựng các phần của biểu thức từ trái sang phải.) –

+0

Tôi vô tình giả định cách suy luận kiểu từ trái sang phải, nhưng bị mắc kẹt với quyền ưu tiên của toán tử và sự kết hợp của các tên phương thức, đặc biệt là bắt đầu với '?' trước bất kỳ char alphanum nào khác như một tên phương thức char đầu tiên và một ':' cho sự kết hợp trái. Vì vậy, tôi phải suy nghĩ lại về các tên phương thức mới để có được suy luận kiểu làm việc từ trái sang phải. cảm ơn! –

Trả lời

20

Chúng tôi có thể kết hợp How to define a ternary operator in Scala which preserves leading tokens? với câu trả lời cho Is Option wrapping a value a good pattern? để có được

scala> "Hi".getClass.getSimpleName |> {x => x.endsWith("$") ? x.init | x} 
res0: String = String 

scala> List.getClass.getSimpleName |> {x => x.endsWith("$") ? x.init | x} 
res1: String = List 

Đây có phải là đủ cho nhu cầu của bạn?

+0

Thats rất gần với những gì tôi có trong tâm trí. cách tiếp cận tốt đẹp. Tôi sẽ nghĩ về điều đó. Lý do của tôi để tránh mã đầu tiên là ngắn gọn hơn trong việc không có một 'val' tạm thời cho một câu lệnh' if' sau đây: Làm cho nó dễ hiểu trong một dòng, giống như ta có nó trong đầu. –

13

Rex Kerr’s answer bày tỏ trong Scala cơ bản:

"Hi".getClass.getSimpleName match { 
    case x if x.endsWith("$") => x.init 
    case x => x 
} 

mặc dù tôi không chắc chắn những gì một phần của if-else xây dựng bạn muốn tối ưu hóa.

+0

cách rất thẳng. đôi khi một người quên về các câu lệnh về trường hợp/trường hợp sử dụng hàng ngày. Tôi chỉ gắn bó với một dòng thứ ba nếu thành ngữ khác, nhưng đó thực sự là một cách dễ hiểu để giải quyết. –

+0

Cân đối mô hình dễ dàng với hơn hai nhánh. – Raphael

100

Từ Tony Morris' Lambda Blog:

Tôi nghe câu hỏi này rất nhiều. Có nó. Thay vì c ? p : q, nó được viết if(c) p else q.

Điều này có thể không thích hợp hơn. Có lẽ bạn muốn viết nó bằng cách sử dụng cú pháp tương tự như Java. Đáng buồn thay, bạn không thể. Điều này là do : không phải là số nhận dạng hợp lệ. Đừng sợ, |! Bạn sẽ giải quyết cho điều này?

c ? p | q 

Sau đó, bạn sẽ cần mã sau đây. Chú ý chú thích từng tên một (=>) trên các đối số. Chiến lược đánh giá này là bắt buộc để viết lại chính xác toán tử bậc ba của Java. Điều này không thể được thực hiện trong Java chính nó.

case class Bool(b: Boolean) { 
    def ?[X](t: => X) = new { 
    def |(f: => X) = if(b) t else f 
    } 
} 

object Bool { 
    implicit def BooleanBool(b: Boolean) = Bool(b) 
} 

Dưới đây là một ví dụ sử dụng các nhà điều hành mới mà chúng ta vừa định nghĩa:

object T { val condition = true 

    import Bool._ 

    // yay! 
    val x = condition ? "yes" | "no" 
} 

Have fun;)

+0

có, tôi đã thấy điều này trước đây, nhưng sự khác biệt là, rằng tôi có giá trị (được đánh giá) của biểu thức đầu tiên của tôi như một đối số trong mệnh đề 'sau đó' và' else'. –

+0

@Imre: Tôi đã sửa liên kết và sao chép nội dung. – Landei

+2

Tôi đã sử dụng phương pháp 'if (c) p else q' ... việc thiếu niềng răng làm cho tôi cảm thấy khó chịu nhưng đó chỉ là một điều phong cách – rjohnston

0

Từ: bởi chính nó sẽ không phải là một nhà điều hành có hiệu lực, trừ khi bạn là ok với luôn luôn thoát nó với ve sau :, bạn có thể đi với một nhân vật khác, ví dụ "|" như một trong những câu trả lời ở trên. Nhưng làm thế nào về elvis với một goatee? ::

implicit class Question[T](predicate: => Boolean) { 
    def ?(left: => T) = predicate -> left 
} 
implicit class Colon[R](right: => R) { 
    def ::[L <% R](pair: (Boolean, L)): R = if (q._1) q._2 else right 
} 
val x = (5 % 2 == 0) ? 5 :: 4.5 

Tất nhiên điều này một lần nữa sẽ không hoạt động nếu bạn có giá trị là danh sách, vì họ có :: nhà điều hành bản thân.

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