我实现了像Java <condition> ? <if true> : <if false>
这样的三元运算符,用/
代替:
,因为:
不是有效的标识符:
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
那样工作?我搜索了类似的解决方案,我发现的实现都表现出相同的行为。
答案 0 :(得分:12)
您可以做的一件事是将/
的定义更改为:
def /[U >: T](f: => U) = o getOrElse f
它不像常规if
(推断类型为Double
) - 你得到AnyVal
,但这已经是一个改进,只需对你进行非常小的修改代码。
更新:我认为我发现了一种(稍微复杂一点)的方式,使其表现得更像普通的情况(例如,在这种情况下,推断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
}
答案 1 :(得分:9)
这是我的版本,因为我很好奇。我不会真的用这个......
我选择了低优先级的运营商。 ^
将首先绑定,然后|?
。我使用了元组是协变的事实来推断结果的类型。
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
答案 2 :(得分:2)
我一直在玩@ 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中的一个也需要低优先级,|
除了字母数字之外具有最低优先级。< / p>
我还添加了一个重载,以便我们可以使用另一个Ternary子句。
因此
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
?