Toán tử bậc ba Tương tự với?:


94

Tôi đang cố gắng tránh các cấu trúc như thế này:

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

Ok, trong ví dụ này then, elsenhánh và rất đơn giản, nhưng bạn có thể hình dung những cái 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)
}

Đã xác định điều đó, tôi có thể thay thế ví dụ đơn giản ở trên bằng:

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 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 những thứ bổ sung để suy ra các loại.


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

Tôi vô thức giả định cách suy luận kiểu từ trái sang phải, nhưng bị mắc kẹt với sự ưu tiên của toán tử và tính liên kết của tên phương thức, đặc biệt là bắt đầu bằng ?trước bất kỳ ký tự alphanum nào khác như một tên phương thức ký tự đầu tiên và một :cho phép 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 để suy luận kiểu hoạt động từ trái sang phải. cảm ơn!
Peter Schmitz

Câu trả lời:


28

Chúng ta có thể kết hợp Làm thế nào để xác định một toán tử bậc ba trong Scala bảo tồn các mã thông báo hàng đầu? với câu trả lời cho Liệu việc gói một giá trị có phải là một mô hình tốt không? để 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

Điều này có đủ cho nhu cầu của bạn không?


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

125

Từ Blog Lambda của Tony Morris :

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

Điều này có thể không tốt hơn. Có lẽ bạn muốn viết nó bằng cú pháp giống 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ợ, |là! Bạn sẽ giải quyết cho điều này?

c ? p | q

Sau đó, bạn sẽ cần mã sau. Lưu ý các =>chú thích call-by-name ( ) trên các đối số. Chiến lược đánh giá này được yêu cầu để 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 chính Java.

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) 
}

Đây là một ví dụ sử dụng toán tử mới mà chúng tôi vừa xác định:

object T {   val condition = true

  import Bool._

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

Chúc vui vẻ ;)


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

5
Tôi đã if(c) p else qtiếp cận ... thiếu niềng răng làm cho tôi một cảm thấy không thoải mái nhưng đó chỉ là một điều phong cách
rjohnston

17

Câu trả lời của Rex Kerr được thể hiện bằng 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 phần nào của cấu trúc if – else mà bạn muốn tối ưu hóa.


đường rất thẳng. đôi khi người ta quên các câu lệnh đối sánh / trường hợp sử dụng hàng ngày. Tôi chỉ bám vào if then elsecâu thành ngữ bậc ba một dòng , nhưng đó thực sự là một cách giải quyết dễ hiểu.
Peter Schmitz

1
Mô hình Phù hợp với quy mô dễ dàng đến hơn hai nhánh.
Raphael


0

Vì: bản thân nó sẽ không phải là một toán tử hợp lệ trừ khi bạn đồng ý với việc luôn thoát nó với dấu tích phía sau :, bạn có thể đi với một ký tự khác, ví dụ: "|" như trong một trong những câu trả lời ở trên. Nhưng Elvis với một con dê đực thì sao? ::

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 các giá trị của bạn là danh sách, vì chúng có chính toán tử ::.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.