首页 文章

如何在Scala中定义一个保留前导令牌的三元运算符?

提问于
浏览
8

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

我需要模仿一个三元运算符,使得令牌导致'?'保持不变 .

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

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

c match { case(true) => p; case _ => q }

我找到的另一个选择是:

class ternary(val g: Boolean => Any) { def |: (b:Boolean) = g(b) }

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

这让我写:

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

我喜欢第二个选项的整体外观,但有没有办法让它更简洁?

谢谢

4 回答

  • 14

    即使语法没有以预期的顺序进行评估 - 它将条件绑定到第一个选项! - 您可以像这样创建自己的三元运算符:

    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 对象上的方法,该对象将条件绑定到要在"true"情况下返回的对象 . 生成的 IfTrue 对象现在使用 | 方法作为评估条件的请求,如果条件为真则返回存储的true选项,如果是假则返回刚刚传入的true .

    请注意,我使用的东西如 => 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
    

    (P.S.为了避免与Actor库 ? 混淆,你可能应该把它称为 |? 之类的东西 . )

  • 4

    让我们保持简单:

    Java的:

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

    斯卡拉:

    tmp = if (a > b) a else b
    

    除了简单,它是清晰和快速的,因为:不分配您不需要的对象,保持垃圾收集器不等式(因为它应该是),并更好地利用处理器缓存 .

  • 0

    你可以使用这样的东西

    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 . 它们都有一个名为 > 的方法,它分别返回第二个操作数或第三个操作数 .

  • 14

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

    • 我使用Scala的新值类来避免拳击开销 .

    • 在第2和第3个操作数上调用名称,以便仅评估所选的一个操作数 .

    • Michel在1st(布尔)操作数上调用by-value以避免按名称开销;它总是被评估 .

    • 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个字符 . 对于 ifelsenulltrue ,Scala IDE的关键字语法着色相同(例如紫色),在某些情况下会有更好的对比度(下面的语法着色没有显示当前在此网站上呈现的):

    if (cond) true else null
    cond ? true | null
    

相关问题