三元运算符,类似于?:


94

我试图避免这样的构造:

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

好的,在此示例中,thenand 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}

我猜编译器需要额外的东西来推断类型。


由于我的答案中实际上没有这个问题-您遇到麻烦的原因是类型推断从左到右效果最好,但是由于运算符的优先级,您将标记从右到左绑定在一起。如果您用相同的优先级将所有语句用词组成,然后更改事物组合在一起的方式,则将获得所需的推论。(即你将有HasIsIsWithConditionConditionAndTrueCase,将建立表达的部分从左至右类。)
雷克斯克尔

我在不知不觉中假定了从左到右的类型推断方式,但始终坚持运算符的优先级和方法名称的关联性,尤其是?在以任何其他alphanum char 开头作为方法名称的第一个char和一个:用于左侧关联性的开头。因此,我必须重新考虑新的方法名称,以使类型推断从左到右起作用。谢谢!
彼得·施米兹

Answers:


28

我们可以结合在一起如何在Scala中定义保留前导标记的三元运算符?Option包装值是一个好的模式的答案吗?要得到

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语句的临时语句:在一行中做到可理解,就像在记住它一样。
彼得·施米兹

125

来自Tony Morris的Lambda博客

我经常听到这个问题。是的,它确实。相反c ? p : q,它是书面的if(c) p else q

这可能不是优选的。也许您想使用与Java相同的语法来编写它。可悲的是,你不能。这是因为:不是有效的标识符。不要害怕,|是!你愿意为此吗?

c ? p | q

然后,您将需要以下代码。请注意=>参数上的“ 按名称调用”()批注。要正确重写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) 
}

这是使用我们刚定义的new运算符的示例:

object T {   val condition = true

  import Bool._

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

玩得开心 ;)


是的,我之前已经看过这一点,但是不同之处在于,a我有第一个表达式的(求值)值作为thenand else子句中的参数。
Peter Schmitz

5
我采用了这种if(c) p else q方法...缺乏括号使我感觉
有点

17

Rex Kerr在基本Scala中表达的答案

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

尽管我不确定您想优化if-else构造的哪一部分。


很直的方法。有时,人们会忘记日常使用的比赛/案件陈述。我只是坚持单行三元if then else成语,但这确实是一种可理解的解决方法。
Peter Schmitz

1
模式匹配可以轻松扩展到两个以上的分支。
拉斐尔


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

当然,如果您的值是列表,那么这将再次不起作用,因为它们本身具有::运算符。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.