Skip to content

Latest commit

 

History

History
66 lines (47 loc) · 2.55 KB

for-comprehensions.md

File metadata and controls

66 lines (47 loc) · 2.55 KB
layout title discourse partof num next-page previous-page redirect_from
tour
For Comprehensions
true
scala-tour
17
generic-classes
extractor-objects
/tutorials/tour/for-comprehensions.html

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's an example:

case class User(name: String, age: Int)

val userBase = List(User("Travis", 28),
  User("Kelly", 33),
  User("Jennifer", 44),
  User("Dennis", 23))

val twentySomethings = for (user <- userBase if (user.age >=20 && user.age < 30))
  yield user.name  // i.e. add this to a list

twentySomethings.foreach(name => println(name))  // prints Travis Dennis

The for loop used with a yield statement actually creates a List. Because we said yield user.name, it's a List[String]. user <- userBase is our generator and if (user.age >=20 && user.age < 30) is a guard that filters out users who are not in their 20s.

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

def foo(n: Int, v: Int) =
   for (i <- 0 until n;
        j <- 0 until n if i + j == v)
   yield (i, j)

foo(10, 10) foreach {
  case (i, j) =>
    println(s"($i, $j) ")  // prints (1, 9) (2, 8) (3, 7) (4, 6) (5, 5) (6, 4) (7, 3) (8, 2) (9, 1)
}

Here n == 10 and v == 10. On the first iteration, i == 0 and j == 0 so i + j != v and therefore nothing is yielded. j gets incremented 9 more times before i gets incremented to 1. Without the if guard, this would simply print the following:


(0, 0) (0, 1) (0, 2) (0, 3) (0, 4) (0, 5) (0, 6) (0, 7) (0, 8) (0, 9) (1, 0) ...

Note that comprehensions are not restricted to lists. Every datatype that supports the operations withFilter, map, and flatMap (with the proper types) can be used in sequence comprehensions.

You can omit yield in a comprehension. In that case, comprehension will return Unit. This can be useful in case you need to perform side-effects. Here's a program equivalent to the previous one, but without using yield:

def foo(n: Int, v: Int) =
   for (i <- 0 until n;
        j <- 0 until n if i + j == v)
   println(s"($i, $j)")

foo(10, 10)