如何在 Scala 中定义保留前导标记的三元运算符?

发布于 2024-08-30 07:18:04 字数 611 浏览 4 评论 0原文

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

我需要以这样的方式模拟三元运算符,即导致“?”的标记保持完好无损。

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

我的第一次尝试(仍然使用 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 }

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

谢谢?

I'm writing a code generator which produces Scala output.

I need to emulate a ternary operator in such a way that the tokens leading up to '?' remain intact.

e.g. convert the expression c ? p : q to c something. The simple if(c) p else q fails my criteria, as it requires putting if( before c.

My first attempt (still using c/p/q as above) is

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

another option I found was:

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

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

which allows me to write:

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

I like the overall look of the second option, but is there a way to make it less verbose?

Thanks

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

避讳 2024-09-06 07:18:04

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

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(按名称参数),以便除非实际使用表达式,否则不会计算表达式。因此,您将只评估您实际需要的一面(就像 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 库 ? 混淆,您可能应该将其称为 |? 之类的其他名称。)

Even though the syntax doesn't evaluate in the expected order--it binds the conditional to the first option!--you can make your own ternary operator like this:

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)

The trick is to interpret ? as a method on a MakeIfTrue object that binds the condition to the object to return in the "true" case. The resulting IfTrue object now uses the | method as a request to evaluate the condition, returning the stored true option if the condition is true, or the just-passed-in one if it's false.

Note that I've used stuff like => A instead of just A--by-name parameters--in order to not evaluate the expression unless it's actually used. Thus, you'll only evaluate the side that you actually need (just like an if statement).

Let's see it in action:

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. To avoid confusion with the Actor library ?, you probably ought to call it something else like |?.)

滥情稳全场 2024-09-06 07:18:04

让我们保持简单:

Java:

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

Scala:

tmp = if (a > b) a else b

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

Let's keep it simple:

Java:

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

Scala:

tmp = if (a > b) a else b

Besides simplicity, it is clear and fast because: do not allocate objects you don't need, keeps the garbage collector out of equation (as it always should be) and makes better use of processor caches.

护你周全 2024-09-06 07:18:04

您可以使用类似这样的

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"

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

You could use something like this

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"

This code converts any boolean value to an anonymous type that has a method called ?. Depending on the value of the boolean, this method will either return TernarySecond or TernaryThird. They both have a method called > which returns the second operand or the third one respectively.

与君绝 2024-09-06 07:18:04

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

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

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 的关键字语法颜色对于 ifelsenulltrue,在某些情况下有更好的对比度(本网站当前呈现的语法着色未显示这一点):

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

Ternary operator which adds my improvement to the best of Rex Kerr’s and Michel Krämer’s implementations:

  • My improvement to use Scala’s new value class to avoid boxing overhead.
  • Call by-name on 2nd and 3rd operands so only the chosen one is evaluated.
  • Michel’s call by-value on the 1st (Boolean) operand to avoid by-name overhead; it is always evaluated.
  • Rex’s concrete class for the condition to avoid any anonymous class overhead.
  • Michel’s evaluation of the condition to determine which class to construct to avoid of overhead of a two argument constructor.

.

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

Note the improvement over if else is not just the 6 characters saved. With Scala IDE’s syntax coloring on keywords being the same (e.g. purple) for if, else, null, and true, there is better contrast in some cases (which isn't shown by the syntax coloring below as currently rendered on this site):

if (cond) true else null
cond ? true | null
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文