三元运算符输入

发布于 2024-12-12 17:01:14 字数 853 浏览 1 评论 0原文

我实现了一个三元运算符,如 Java 的 ; ? <如果为真> :,用 / 替换 :,因为 : 不是有效的标识符:

case class Ternary[T](val o: Option[T]) {
  def / (f: => T) = o getOrElse f
}

implicit def boolToTernary(cond: Boolean) = new {
  def ? [T](f: => T) = if(cond) Ternary(Some(f)) 
                        else    Ternary[T](None)
}

一般情况下工作正常,例如

scala> (1 > 2) ? "hi" / "abc"
res9: java.lang.String = abc

,但在以下情况下会失败:

scala> (1 > 2) ? 5 / 6.0
<console>:33: error: type mismatch;
 found   : Double(6.0)
 required: Int
       (1 > 2) ? 5 / 6.0
                     ^

是否可以对类型进行任何调整,以便使其像内置 if (1 > 2) 5 else 6.0 一样工作?我在谷歌上搜索了类似的解决方案,发现所有的实现都表现出相同的行为。

I implemented a ternary operator like Java's <condition> ? <if true> : <if false>, substituting / for :, since : is not a valid identifier:

case class Ternary[T](val o: Option[T]) {
  def / (f: => T) = o getOrElse f
}

implicit def boolToTernary(cond: Boolean) = new {
  def ? [T](f: => T) = if(cond) Ternary(Some(f)) 
                        else    Ternary[T](None)
}

It works fine in general, e.g.

scala> (1 > 2) ? "hi" / "abc"
res9: java.lang.String = abc

but falls down in the following case:

scala> (1 > 2) ? 5 / 6.0
<console>:33: error: type mismatch;
 found   : Double(6.0)
 required: Int
       (1 > 2) ? 5 / 6.0
                     ^

Is there any tweaking I can do to the types in order to get this to work like the built-in if (1 > 2) 5 else 6.0 does? I googled for similar solutions and the implementations I found all exhibited the same behaviour.

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

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

发布评论

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

评论(3

醉城メ夜风 2024-12-19 17:01:15

您可以做的一件事是将 / 的定义更改为:

def /[U >: T](f: => U) = o getOrElse f

它不像常规 if 那样工作(其中推断类型为 Double) — 您得到 AnyVal,但这已经是一项改进,只需对代码进行很小的修改。

更新:我想我已经找到了一种(稍微复杂一些)方法,使其表现得更像正常的 if (例如,在本例中,推断 Double)。试试这个代码:

implicit def boolToTernary(cond: Boolean) = new {
  def ?[T](f: => T) = if (cond) Ternary(Some(f))
  else Ternary[T](None)
}

case class Ternary[T](val o: Option[T]) {
  def /[U, That](f: => U)(implicit conv: BiConverter[T, U, That]): That = o map conv.toThat1 getOrElse (conv toThat2 f)
}

class BiConverter[T, U, That](val toThat1: T => That, val toThat2: U => That)

trait LowPriorityBiConverterImplicits {
  implicit def subtype[A, T <: A, U <: A]: BiConverter[T, U, A] = new BiConverter[T, U, A](identity[T], identity[U])
}

object BiConverter extends LowPriorityBiConverterImplicits {
  implicit def identityConverter[T]: BiConverter[T, T, T] = new BiConverter[T, T, T](identity, identity)
  implicit def firstAsSecond[T, U](implicit conv: T => U): BiConverter[T, U, U] = new BiConverter[T, U, U](conv, identity)
  implicit def secondAsFirst[T, U](implicit conv: U => T): BiConverter[T, U, T] = new BiConverter[T, U, T](identity, conv)
}

然后(一些示例代码):

abstract class Fruit
class Apple extends Fruit
class Banana extends Fruit

def main(args: Array[String]) {
  val int = (1 > 2) ? 5 / 6 // Int is inferred
  val fruit = (1 > 2) ? new Apple / new Banana // Fruit is inferred
  val double1 = (1 > 2) ? 5 / 5.5 // Double is inferred
  val double2 = (1 > 2) ? 5.5 / 5 // Double is inferred
}

One thing you can do is change your definition of / to this:

def /[U >: T](f: => U) = o getOrElse f

It doesn't work like a regular if (where the inferred type would be Double) — you get AnyVal, but that's already an improvement with a very small modification to your code.

Update: I think I've found a (slightly more complicated) way to make it behave more like a normal if (e.g., in this case, inferring a Double). Try this code:

implicit def boolToTernary(cond: Boolean) = new {
  def ?[T](f: => T) = if (cond) Ternary(Some(f))
  else Ternary[T](None)
}

case class Ternary[T](val o: Option[T]) {
  def /[U, That](f: => U)(implicit conv: BiConverter[T, U, That]): That = o map conv.toThat1 getOrElse (conv toThat2 f)
}

class BiConverter[T, U, That](val toThat1: T => That, val toThat2: U => That)

trait LowPriorityBiConverterImplicits {
  implicit def subtype[A, T <: A, U <: A]: BiConverter[T, U, A] = new BiConverter[T, U, A](identity[T], identity[U])
}

object BiConverter extends LowPriorityBiConverterImplicits {
  implicit def identityConverter[T]: BiConverter[T, T, T] = new BiConverter[T, T, T](identity, identity)
  implicit def firstAsSecond[T, U](implicit conv: T => U): BiConverter[T, U, U] = new BiConverter[T, U, U](conv, identity)
  implicit def secondAsFirst[T, U](implicit conv: U => T): BiConverter[T, U, T] = new BiConverter[T, U, T](identity, conv)
}

Then (some sample code):

abstract class Fruit
class Apple extends Fruit
class Banana extends Fruit

def main(args: Array[String]) {
  val int = (1 > 2) ? 5 / 6 // Int is inferred
  val fruit = (1 > 2) ? new Apple / new Banana // Fruit is inferred
  val double1 = (1 > 2) ? 5 / 5.5 // Double is inferred
  val double2 = (1 > 2) ? 5.5 / 5 // Double is inferred
}
要走干脆点 2024-12-19 17:01:15

这是我的版本只是因为我很好奇。我不会真正使用这个...

我选择了低优先级运算符。 ^ 将首先绑定,然后是 |?。我利用元组是协变的事实来推断结果的类型。

case class TernClause[T](t: T) {
  def ^[U](u: U) = (t, u)
}
case class Tern(b: Boolean) {
  def |?[U](tuple: (U,U)) = if (b) tuple._1 else tuple._2
}

implicit def toTern(b: Boolean): Tern = Tern(b)
implicit def toTernClause[T](t: T): TernClause[T] = TernClause(t)

(1 > 2) |? "hi" ^ "abc"
// java.lang.String = abc

(1 > 2) |? 5 ^ 6.0
// AnyVal{def getClass(): java.lang.Class[_ >: Double with Int <: AnyVal]} = 6.0

另一个例子展示了运算符优先级如何协同工作:

3 > 2 |? 5 - 1 ^ 6.0 + 1
// AnyVal{def getClass(): java.lang.Class[_ >: Double with Int <: AnyVal]} = 4

可能需要做一些工作来确保未使用的分支不会被评估。

仅供思考:请注意,调用函数时类型推断会做正确的事情。不太灵活的语法适合您吗?它确实简单得多...

class BooleanEx(b: Boolean) {
  def ?[U](onTrue: => U, onFalse: => U) = if (b) onTrue else onFalse
}

implicit def toBooleanEx(b: Boolean): BooleanEx = new BooleanEx(b)

class A
class B extends A

(1 > 2) ? ("hi", "abc")
//res0: java.lang.String = abc
(1 > 2) ? (5, 6.0)
//res1: Double = 6.0
(1 > 2) ? (5, 6)
//res2: Int = 6
(1 > 2) ? (new A, new B)
//res3: A = B@1e21540

此外,这在 scalaz 中可用,但他们将其命名为 fold

import scalaz._
import Scalaz._
(1 > 2) fold (5, 6.0)
//res0: Double = 6.0

Here is my version just cause I'm curious. I wouldn't really use this...

I chose low priority operators. ^ will bind first, then |?. I use the fact that tuples are covariant to infer the type of the result.

case class TernClause[T](t: T) {
  def ^[U](u: U) = (t, u)
}
case class Tern(b: Boolean) {
  def |?[U](tuple: (U,U)) = if (b) tuple._1 else tuple._2
}

implicit def toTern(b: Boolean): Tern = Tern(b)
implicit def toTernClause[T](t: T): TernClause[T] = TernClause(t)

(1 > 2) |? "hi" ^ "abc"
// java.lang.String = abc

(1 > 2) |? 5 ^ 6.0
// AnyVal{def getClass(): java.lang.Class[_ >: Double with Int <: AnyVal]} = 6.0

Another example showing how operator precedence works together:

3 > 2 |? 5 - 1 ^ 6.0 + 1
// AnyVal{def getClass(): java.lang.Class[_ >: Double with Int <: AnyVal]} = 4

Probably some work needed to ensure the unused branch does not get evaluated.

Just food for thoughts: note that type inference does the right thing when calling a function. Would a less flexible syntax work for you? It's really a lot simpler...

class BooleanEx(b: Boolean) {
  def ?[U](onTrue: => U, onFalse: => U) = if (b) onTrue else onFalse
}

implicit def toBooleanEx(b: Boolean): BooleanEx = new BooleanEx(b)

class A
class B extends A

(1 > 2) ? ("hi", "abc")
//res0: java.lang.String = abc
(1 > 2) ? (5, 6.0)
//res1: Double = 6.0
(1 > 2) ? (5, 6)
//res2: Int = 6
(1 > 2) ? (new A, new B)
//res3: A = B@1e21540

Also, this is available in scalaz but they name it fold:

import scalaz._
import Scalaz._
(1 > 2) fold (5, 6.0)
//res0: Double = 6.0
零時差 2024-12-19 17:01:15

我一直在研究@Jean-Philippe 的解决方案,并添加了一些内容以允许链接运算符。 (好吧,我可以保持原样并使用括号,但这有什么乐趣呢?)可能有更好的方法来做到这一点,因此欢迎提出改进建议。

implicit def boolToTernary(cond: Boolean) = new {
  // operator name changed
  def |? [T](f: => T) = if (cond) Ternary(Some(f)) else Ternary[T](None)
}

case class Ternary[T](o: Option[T]) {
  def or [U, That](f: => U)      (implicit conv: BiConverter[T, U, That]): That = o map conv.toThat1 getOrElse (conv toThat2 f)
  // overload added
  def or [U, That](t: Ternary[U])(implicit conv: BiConverter[T, U, That]): Ternary[That] = o match {
    case x: Some[_] => Ternary(o map conv.toThat1)
    case None       => Ternary(t.o map conv.toThat2)
  }
}

我更改了运算符名称:Ternary 类中的运算符需要具有较低优先级,但隐式 def 中的运算符也需要具有较低优先级,并且 | 具有除字母数字之外的最低优先级。

我还添加了一个重载,以便我们可以采用另一个三元子句。

因此

1 > 2 |? 4 or 4 > 6 |? 8.0 or 10  //look, no parentheses!
// Double = 10.0

1 + 1 < 3 |?
  4L or
  4 > 6 |?
    8 or BigInt(10)
// scala.math.BigInt = 4

很酷还是什么? :) 谁需要 if / else 呢?

I've been playing around with @Jean-Philippe's solution, and made a couple of additions to allow the chaining of operators. (Well I could have just left it as it is and used parentheses, but where's the fun in that?) There's probably a better way to do this, so suggestions for improvements are welcome.

implicit def boolToTernary(cond: Boolean) = new {
  // operator name changed
  def |? [T](f: => T) = if (cond) Ternary(Some(f)) else Ternary[T](None)
}

case class Ternary[T](o: Option[T]) {
  def or [U, That](f: => U)      (implicit conv: BiConverter[T, U, That]): That = o map conv.toThat1 getOrElse (conv toThat2 f)
  // overload added
  def or [U, That](t: Ternary[U])(implicit conv: BiConverter[T, U, That]): Ternary[That] = o match {
    case x: Some[_] => Ternary(o map conv.toThat1)
    case None       => Ternary(t.o map conv.toThat2)
  }
}

I changed the operator names: the one in the Ternary class needs to be lower priority, but the one in the implicit def also needs to be low priority, and | has the lowest priority other than alphanumerics.

Also I added an overload, so that we can take another Ternary clause.

Hence

1 > 2 |? 4 or 4 > 6 |? 8.0 or 10  //look, no parentheses!
// Double = 10.0

1 + 1 < 3 |?
  4L or
  4 > 6 |?
    8 or BigInt(10)
// scala.math.BigInt = 4

Cool or what? :) Who needs if / else anyway?

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