首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >三元运算符类型

三元运算符类型
EN

Stack Overflow用户
提问于 2011-10-29 05:55:57
回答 3查看 726关注 0票数 9

我实现了一个三元运算符,比如Java的<condition> ? <if true> : <if false>,用/代替:,因为:不是有效的标识符:

代码语言:javascript
复制
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)
}

一般来说,它工作得很好,例如

代码语言:javascript
复制
scala> (1 > 2) ? "hi" / "abc"
res9: java.lang.String = abc

但在以下情况下会失败:

代码语言:javascript
复制
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一样工作,我可以对类型做一些调整吗?我在谷歌上搜索了类似的解决方案,我发现的实现都表现出了相同的行为。

EN

回答 3

Stack Overflow用户

回答已采纳

发布于 2011-10-29 14:06:47

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

代码语言:javascript
复制
def /[U >: T](f: => U) = o getOrElse f

它的工作方式不同于常规的if (其中推断的类型将是Double) -您得到的是AnyVal,但这已经是一个改进,只需对您的代码进行非常小的修改。

更新:我想我已经找到了一种(稍微复杂一点)的方法,可以让它的行为更像一个正常的if (例如,在这种情况下,推断一个Double)。尝试以下代码:

代码语言:javascript
复制
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)
}

然后(一些示例代码):

代码语言:javascript
复制
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
}
票数 12
EN

Stack Overflow用户

发布于 2011-10-29 09:45:14

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

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

代码语言:javascript
复制
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

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

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

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

只是思想的食粮:注意到类型推断在调用函数时做了正确的事情。不太灵活的语法对你有用吗?真的简单多了..。

代码语言:javascript
复制
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

代码语言:javascript
复制
import scalaz._
import Scalaz._
(1 > 2) fold (5, 6.0)
//res0: Double = 6.0
票数 9
EN

Stack Overflow用户

发布于 2011-10-31 08:46:01

我一直在尝试@Jean-Philippe的解决方案,并添加了几个操作符链。(好吧,我可以让它保持原样并使用括号,但这有什么意思呢?)可能有一种更好的方法来做到这一点,因此欢迎提出改进建议。

代码语言:javascript
复制
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)
  }
}

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

此外,我还添加了一个重载,以便我们可以接受另一个三元子句。

因此

代码语言:javascript
复制
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呢?

票数 2
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/7935315

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档