Sequence Comprehensions

Scala offers a lightweight notation for expressing sequence comprehensions. Comprehensions have the form for (enumerators) yield e, where enumerators refers to a semicolon-separated list of enumerators. An enumerator is either a generator which introduces new variables, or it is a filter. A comprehension evaluates the body e for each binding generated by the enumerators and returns a sequence of these values.

Here is an example:

object ComprehensionTest1 extends App {
  def even(from: Int, to: Int): List[Int] =
    for (i <- List.range(from, to) if i % 2 == 0) yield i
  Console.println(even(0, 20))
}

The for-expression in function introduces a new variable i of type Int which is subsequently bound to all values of the list List(from, from + 1, ..., to - 1). The guard if i % 2 == 0 filters out all odd numbers so that the body (which only consists of the expression i) is only evaluated for even numbers. Consequently, the whole for-expression returns a list of even numbers.

The program yields the following output:

List(0, 2, 4, 6, 8, 10, 12, 14, 16, 18)

Here is a more complicated example which computes all pairs of numbers between 0 and n-1 whose sum is equal to a given value v:

object ComprehensionTest2 extends App {
  def foo(n: Int, v: Int) =
    for (i <- 0 until n;
         j <- i until n if i + j == v) yield
      Pair(i, j);
  foo(20, 32) foreach {
    case (i, j) =>
      println("(" + i + ", " + j + ")")
  }
}

This example shows that comprehensions are not restricted to lists. The previous program uses iterators instead. Every datatype that supports the operations filterWith, map, and flatMap (with the proper types) can be used in sequence comprehensions.

Here's the output of the program:

(13, 19)
(14, 18)
(15, 17)
(16, 16)

There is also a special form of sequence comprehension which returns Unit. Here the bindings that are created from the list of generators and filters are used to perform side-effects. The programmer has to omit the keyword yield to make use of such a sequence comprehension. Here's a program which is equivalent to the previous one but uses the special for comprehension returning Unit:

object ComprehensionTest3 extends App {
  for (i <- Iterator.range(0, 20);
       j <- Iterator.range(i, 20) if i + j == 32)
    println("(" + i + ", " + j + ")")
}
blog comments powered by Disqus