Ternary Operators in Scala

2019-06-14 19:11发布

I would like to simplify this:

var countA: Int = 0
var countB: Int = 0

if (validItem) {
  if (region.equalsIgnoreCase( "US" )) {
    if (itemList > 0) {
      countB = 1
    } else {
      countA = 1
    }
  } else {
    countB = 1
  }
} else {
  countA = 1
}

How do I use ternary operator in scala.

4条回答
Luminary・发光体
2楼-- · 2019-06-14 19:34

I think the short answer is that in Scala there is no ?: ternary operator. Although you can imitate the syntax using implicits (see @jwvh's answer), I think it doesn't really simplify anything.

There are a couple of important properties of the conventional ?:

  1. it always has two branches
  2. following from the previous property, the ternary operator always returns a value (this is mostly the point of using ?:)

    val result: Int = if (true) 1 else 2
    // result is 1
    
  3. branches are evaluated lazily

    if (true) 1 else (0/0) // returns 1
    if (false) 0/0 else 2  // returns 2
    // i.e. 0/0 is not evaluated
    

As you see, in Scala if-else (with else) construction satisfies these properties. This is not the case for if-else construction in some other languages, like C or Java, because it doesn't return a value.

So the bottom line is that in Scala you don't need a ternary operator, because you can just use if-else.

UPDATE

As Alexey Romanov mentions in the comments, if statement without else actually satisfies the first condition as well. When you write

val result = if (true) 1

it actually means if (true) 1 else (), so result will have type AnyVal instead of Int, because the return type of the if expression is the lowest common bound of the both branches (Int and Unit in this case).

查看更多
别忘想泡老子
3楼-- · 2019-06-14 19:38

This might be a bit confusing for a "newbie", but you could attach a ternary method to the Boolean class like so.

implicit class Ternary[T](condition: Boolean) {
  def ??(a: => T, b: => T): T = if (condition) a else b
}

Usage:

(4 == 4)??("yes","no")         // res0: String = yes
("abc".length < 2).??(1,0)     // res1: Int = 0
List('c').isEmpty.??('X','+')  // res2: Char = +
查看更多
可以哭但决不认输i
4楼-- · 2019-06-14 19:39

To expand on @0__'s answer (if that is his/her real name), you can also use tuples to assign to two variables at once.

val (countA, countB) = 
  if (validItem) {
    if (region.equalsIgnoreCase("US")) {
      if (itemList > 0) (0,1) else (1,0)
    } else {
      (0,1)
    }
  } else {
    (1,0)
  }
查看更多
一纸荒年 Trace。
5楼-- · 2019-06-14 19:45

You should not need to use a ternary operator in Scala. In Scala, if is an expression not a statement, you can say val x = if (b) 1 else 2.

The usage of var in your example also points to a problem, because you can usually avoid this when you use the if as an expression.

Let's try to break down the code to avoid the var, i.e. first remove all if statements that are not expressions with a corresponding else and always provide both values:

var countA: Int = ???
var countB: Int = ???

if (validItem) {
  if (region.equalsIgnoreCase("US")) {
    if (itemList > 0) {
      countA = 0
      countB = 1
    } else {
      countA = 1
      countB = 0
    }
  } else {
    countA = 0
    countB = 1
  }
} else {
  countA = 1
  countB = 0
}

Now we can define the condition for which either of countA and countB is one:

val isUS     = region.equalsIgnoreCase("US")
val hasItems = itemList > 0
val isA      = !validItem || (isUS && !hasItems)
val isB      = !isA
// or: val isB = validItem && (!isUS || hasItems)

and then:

val countA   = if (isA) 1 else 0
val countB   = if (isB) 1 else 0
查看更多
登录 后发表回答