9

我正在编写一个生成 Scala 输出的代码生成器。

我需要以这样的方式模拟三元运算符,即通向“?”的标记 保持不变。

例如将表达式转换c ? p : qc something. 简单if(c) p else q不符合我的标准,因为它需要放在if(之前c

我的第一次尝试(仍然使用上面的 c/p/q)是

c 匹配 { case(true) => p; 案例_ => q }

我发现的另一个选择是:

三元类(val g: Boolean => Any){ def |: (b:Boolean) = g(b) }

隐式 def autoTernary (g: Boolean => Any): ternary = new ternary(g)

这让我可以写:

c |: { b: Boolean => if(b) p else q }

我喜欢第二个选项的整体外观,但有没有办法让它不那么冗长?

谢谢

4

4 回答 4

16

即使语法没有按照预期的顺序进行计算——它将条件绑定到第一个选项!——你可以像这样制作自己的三元运算符:

class IfTrue[A](b: => Boolean, t: => A) { def |(f: => A) = if (b) t else f }
class MakeIfTrue(b: => Boolean) { def ?[A](t: => A) = new IfTrue[A](b,t) }
implicit def autoMakeIfTrue(b: => Boolean) = new MakeIfTrue(b)

诀窍是解释?为对象上的一种方法,该方法MakeIfTrue将条件绑定到对象以在“真”情况下返回。结果IfTrue对象现在使用该|方法作为评估条件的请求,如果条件为真,则返回存储的真选项,如果条件为假,则返回刚刚传入的选项。

请注意,我使用了类似的东西,=> A而不仅仅是A--by-name 参数 -- 为了不评估表达式,除非它被实际使用。因此,您将只评估您实际需要的一侧(就像 if 语句一样)。

让我们看看它的实际效果:

scala> List(1,3,2).isEmpty ? "Empty" | "Nonempty"
res0: java.lang.String = Nonempty

scala> (4*4 > 14) ? true | false
res1: Boolean = true

scala> class Scream(s: String) { println(s.toUpperCase + "!!!!") }
defined class Scream

scala> true ? new Scream("true") | new Scream("false")
TRUE!!!!
res3: Scream = Scream@1ccbdf7

(PS 为避免与 Actor 库混淆?,您可能应该将其称为其他名称|?。)

于 2010-04-24T20:44:37.153 回答
13

让我们保持简单:

爪哇:

tmp = (a > b) ? a : b;

斯卡拉:

tmp = if (a > b) a else b

除了简单之外,它还清晰快速,因为:不要分配您不需要的对象,使垃圾收集器远离等式(因为它总是应该如此)并更好地利用处理器缓存。

于 2011-08-07T19:27:37.327 回答
5

你可以使用这样的东西

sealed trait TernaryOperand[A] {
  def >(q: => A): A
}

case class TernarySecond[A](val p: A) extends TernaryOperand[A] {
  def >(q: => A) = p
}

case class TernaryThird[A]() extends TernaryOperand[A] {
  def >(q: => A) = q
}

implicit def ternary(c: Boolean) = new {
  def ?[A](p: => A): TernaryOperand[A] = if (c) TernarySecond(p) else TernaryThird()
}

val s1 = true ? "a" > "b"
println(s1) //will print "a"

val s2 = false ? "a" > "b"
println(s2) //will print "b"

此代码将任何布尔值转换为具有名为 的方法的匿名类型?。根据布尔值的不同,此方法将返回TernarySecondTernaryThird。它们都有一个方法调用>,分别返回第二个操作数或第三个操作数。

于 2010-04-24T20:45:21.567 回答
2

三元运算符将我的改进添加到 Rex Kerr 和 Michel Krämer 的最佳实现中:

  • 我使用 Scala 的新值类来避免装箱开销的改进。
  • 在第二个和第三个操作数上按名称调用,因此只评估所选的一个。
  • Michel 在第一个(布尔)操作数上按值调用以避免按名称开销;它总是被评估。
  • Rex 的具体类用于避免任何匿名类开销的条件。
  • Michel 对条件的评估以确定构造哪个类以避免两个参数构造函数的开销。

.

sealed trait TernaryResult[T] extends Any {
  def |(op3: => T): T
}

class Ternary2ndOperand[T](val op2: T) extends AnyVal with TernaryResult[T] {
  def |(op3: => T) = op2
}

class Ternary3rdOperand[T](val op2: T) extends AnyVal with TernaryResult[T] {
  def |(op3: => T) = op3
}

class Ternary(val op1:Boolean) extends AnyVal {
   def ?[A](op2: => A): TernaryResult[A] = if (op1) new Ternary2ndOperand(op2) else new Ternary3rdOperand(op2)
}

object Ternary {
   implicit def toTernary(condition: Boolean) = new Ternary(condition)
}

请注意,改进if else不仅仅是节省了 6 个字符。由于 Scala IDE 的关键字语法着色对于ifelsenull和是相同的(例如紫色) true,因此在某些情况下会有更好的对比(本网站当前呈现的下面的语法着色未显示):

if (cond) true else null
cond ? true | null
于 2014-11-14T09:05:09.877 回答