开发者

Declaring multiple variables in Scala

I'd like to use val to开发者_如何学Go declare multiple variable like this:

val a = 1, b = 2, c = 3

But for whatever reason, it's a syntax error, so I ended up using either:

val a = 1
val b = 2
val c = 3

or

val a = 1; val b = 2; val c = 3;

I personally find both options overly verbose and kind of ugly.

Is there a better option?

Also, I know Scala is very well thought-out language, so why isn't the val a = 1, b = 2, c = 3 syntax allowed?


The trivial answer is to declare them as tuples:

val (a, b, c) = (1, 2, 3)

What might be interesting here is that this is based on pattern matching. What is actually happens is that you are constructing a tuple, and then, through pattern matching, assigning values to a, b and c.

Let's consider some other pattern matching examples to explore this a bit further:

val DatePattern = """(\d{4})-(\d\d)-(\d\d)""".r
val DatePattern(year, month, day) = "2009-12-30"
val List(rnd1, rnd2, rnd3) = List.fill(3)(scala.util.Random.nextInt(100))
val head :: tail = List.range(1, 10)

object ToInt {
  def unapply(s: String) = try {
    Some(s.toInt)
  } catch {
    case _ => None
  }
}

val DatePattern(ToInt(year), ToInt(month), ToInt(day)) = "2010-01-01"

Just as a side note, the rnd example, in particular, may be written more simply, and without illustrating pattern matching, as shown below.

val rnd1, rnd2, rnd3 = scala.util.Random.nextInt(100)


Daniel's answer nicely sums up the correct way to do this, as well as why it works. Since he already covered that angle, I'll attempt to answer your broader question (regarding language design)...

Wherever possible, Scala strives to avoid adding language features in favor of handling things through existing mechanisms. For example, Scala doesn't include a break statement. However, it's almost trivial to roll one of your own as a library:

case object BreakException extends RuntimeException

def break = throw BreakException

def breakable(body: =>Unit) = try {
  body
} catch {
  case BreakException => ()
}

This can be used in the following way:

breakable {
  while (true) {
    if (atTheEnd) {
      break
    }

    // do something normally
  }
}

(note: this is included in the standard library for Scala 2.8)

Multiple assignment syntaxes such as are allowed by languages like Ruby (e.g. x = 1, y = 2, z = 3) fall into the category of "redundant syntax". When Scala already has a feature which enables a particular pattern, it avoids adding a new feature just to handle a special case of that pattern. In this case, Scala already has pattern matching (a general feature) which can be used to handle multiple assignment (by using the tuple trick outlined in other answers). There is no need for it to handle that particular special case in a separate way.

On a slightly different aside, it's worth noting that C's (and thus, Java's) multiple assignment syntax is also a special case of another, more general feature. Consider:

int x = y = z = 1;

This exploits the fact that assignment returns the value assigned in C-derivative languages (as well as the fact that assignment is right-associative). This is not the case in Scala. In Scala, assignment returns Unit. While this does have some annoying drawbacks, it is more theoretically valid as it emphasizes the side-effecting nature of assignment directly in its type.


I'll add one quirk here, because it hit myself and might help others.

When using pattern matching, s.a. in declaring multiple variables, don't use Capital names for the variables. They are treated as names of classes in pattern matching, and it applies here as well.

  val (A,B)= (10,20)    // won't work
  println(A)

Error message does not really tell what's going on:

src/xxx.scala:6: error: not found: value A
  val (A,B)= (10,20)
       ^ 
src/xxx.scala:6: error: not found: value B
  val (A,B)= (10,20)
         ^ 
src/xxx.scala:7: error: not found: value A
  println(A)
          ^

I thought `-ticking would solve the issue but for some reason does not seem to (not sure, why not):

  val (`A`,`B`)= (10,20)
  println(A)

Still the same errors even with that.

Please comment if you know how to use tuple-initialization pattern with capital variable names.


If all your variables are of the same type and take same initial value, you could do this.

val a, b, c: Int = 0;


It seems to work if you declare them in a tuple

scala> val (y, z, e) = (1, 2, 45)
y: Int = 1
z: Int = 2
e: Int = 45
scala> e
res1: Int = 45

Although I would probably go for individual statements. To me this looks clearer:

val y = 1
val z = 2
val e = 45

especially if the variables are meaningfully named.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜