Ternary Operator คล้ายกับ?:


94

ฉันพยายามหลีกเลี่ยงโครงสร้างเช่นนี้:

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

ตกลงในตัวอย่างนี้thenและelseสาขาเป็นเรื่องง่าย แต่คุณสามารถสร้างภาพที่ซับซ้อนได้ ฉันสร้างสิ่งต่อไปนี้:

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

กำหนดว่าฉันสามารถแทนที่ตัวอย่างง่ายๆข้างต้นด้วย:

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

แต่ฉันจะกำจัดได้s: String =>อย่างไร? ฉันต้องการอะไรแบบนั้น:

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

ฉันเดาว่าคอมไพเลอร์ต้องการสิ่งพิเศษในการอนุมานประเภท


เนื่องจากฉันไม่ได้มีสิ่งนี้ในคำตอบของฉัน - สาเหตุที่คุณมีปัญหาคือการอนุมานประเภทนั้นทำงานได้ดีที่สุดจากซ้ายไปขวา แต่คุณผูกโทเค็นเข้าด้วยกันจากขวาไปซ้ายเนื่องจากความสำคัญของตัวดำเนินการ หากคุณสร้างข้อความทั้งหมดของคุณเป็นคำ (โดยมีลำดับความสำคัญเหมือนกัน) และเปลี่ยนวิธีการรวมกลุ่มกันคุณจะได้การอนุมานตามที่คุณต้องการ (เช่นคุณจะมีHasIs, IsWithCondition, ConditionAndTrueCaseชั้นเรียนที่จะสร้างขึ้นเป็นส่วนหนึ่งของการแสดงออกจากซ้ายไปขวา).
เร็กซ์เคอร์

ฉันสันนิษฐานวิธีการอนุมานประเภทจากซ้ายไปขวาโดยไม่รู้ตัว แต่ติดอยู่กับลำดับความสำคัญของตัวดำเนินการและการเชื่อมโยงของชื่อวิธีการโดยเฉพาะอย่างยิ่งการเริ่มต้น?ก่อนอักขระตัวอักษรและตัวเลขอื่น ๆ เป็นชื่อวิธีการถ่านแรกและ:สำหรับการเชื่อมโยงด้านซ้าย ดังนั้นฉันจึงต้องคิดใหม่เกี่ยวกับชื่อวิธีการใหม่เพื่อให้การอนุมานประเภททำงานจากซ้ายไปขวา ขอบคุณ!
Peter Schmitz

คำตอบ:


28

เราสามารถรวมHow to define a ternary operator in Scala ซึ่งเก็บรักษาโทเค็นชั้นนำ? พร้อมคำตอบสำหรับการห่อตัวเลือกเป็นรูปแบบที่ดีหรือไม่? ที่จะได้รับ

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

เพียงพอสำหรับความต้องการของคุณหรือไม่?


นั่นเป็นสิ่งที่ใกล้เคียงกับสิ่งที่ฉันคิดไว้มาก แนวทางที่ดี ฉันจะคิดเกี่ยวกับเรื่องนั้น เหตุผลของฉันในการหลีกเลี่ยงรหัสแรกคือต้องกระชับมากขึ้นในการไม่มีชั่วคราวvalสำหรับการแสดงต่อไปนี้if: เข้าใจได้ในบรรทัดเดียวเช่นเดียวกับที่คิดไว้
Peter Schmitz

125

จากบล็อก Lambda ของ Tony Morris :

ฉันได้ยินคำถามนี้มาก ใช่. แทนการจะเขียนc ? p : qif(c) p else q

อาจไม่เป็นที่ต้องการ บางทีคุณอาจต้องการเขียนโดยใช้ไวยากรณ์เดียวกับ Java น่าเศร้าที่คุณทำไม่ได้ เนื่องจาก:ไม่ใช่ตัวระบุที่ถูกต้อง ไม่กลัว|คือ! คุณจะจ่ายสำหรับสิ่งนี้หรือไม่?

c ? p | q

จากนั้นคุณจะต้องใช้รหัสต่อไปนี้ สังเกต=>คำอธิบายประกอบcall-by-name ( ) บนอาร์กิวเมนต์ กลยุทธ์การประเมินนี้จำเป็นสำหรับการเขียนตัวดำเนินการ ternary ของ Java ใหม่อย่างถูกต้อง สิ่งนี้ไม่สามารถทำได้ใน 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) 
}

นี่คือตัวอย่างการใช้ตัวดำเนินการใหม่ที่เราเพิ่งกำหนด:

object T {   val condition = true

  import Bool._

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

มีความสุข ;)


ใช่ฉันเคยเห็นสิ่งนี้มาก่อน แต่ความแตกต่างคือฉันมีค่า (ประเมิน) ของนิพจน์แรกของฉันเป็นอาร์กิวเมนต์ในthenและelseอนุประโยค
Peter Schmitz

5
ฉันใช้if(c) p else qวิธีนี้ ... การขาดเครื่องมือจัดฟันทำให้ฉันรู้สึกอึดอัด แต่นั่นเป็นเพียงสไตล์เท่านั้น
rjohnston

17

คำตอบของ Rex Kerrแสดงใน Scala พื้นฐาน:

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

แม้ว่าฉันจะไม่แน่ใจว่าส่วนใดของโครงสร้าง if – else ที่คุณต้องการเพิ่มประสิทธิภาพ


ทางตรงมาก บางครั้งก็ลืมเกี่ยวกับคำสั่ง match / case ที่ใช้ประจำวัน ฉันแค่ยึดติดกับif then elseสำนวนternary เพียงบรรทัดเดียวแต่มันเป็นวิธีที่เข้าใจได้อย่างแท้จริง
Peter Schmitz

1
Pattern จับคู่เครื่องชั่งได้ง่ายกว่าสองสาขา
Raphael


0

เนื่องจาก: โดยตัวมันเองจะไม่ใช่ตัวดำเนินการที่ถูกต้องเว้นแต่คุณจะพอใจกับการใช้เครื่องหมายขีดหลัง:เสมอคุณสามารถใช้อักขระอื่นเช่น "|" ดังคำตอบข้อใดข้อหนึ่งข้างต้น แต่เอลวิสกับเคราแพะล่ะ ::

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

แน่นอนสิ่งนี้จะไม่ทำงานอีกต่อไปหากค่าของคุณเป็นรายการเนื่องจากมี :: ตัวดำเนินการเอง

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.