-
Notifications
You must be signed in to change notification settings - Fork 1k
Rewrote sequence comprehensions section of tour #754
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 1 commit
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -13,58 +13,39 @@ previous-page: extractor-objects | |
|
||
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: | ||
Here's an example: | ||
|
||
```tut | ||
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. | ||
case class User(val name: String, val age: Int) | ||
|
||
The program yields the following output: | ||
val userBase = List(new User("Travis", 28), | ||
new User("Kelly", 33), | ||
new User("Jennifer", 44), | ||
new 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 | ||
``` | ||
List(0, 2, 4, 6, 8, 10, 12, 14, 16, 18) | ||
``` | ||
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 iterator and `if (user.age >=20 && user.age < 30)` is a guard that filters out users who are 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`: | ||
|
||
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`: | ||
|
||
```tut | ||
object ComprehensionTest2 extends App { | ||
def foo(n: Int, v: Int) = | ||
for (i <- 0 until n; | ||
j <- i until n if i + j == v) yield | ||
(i, j); | ||
foo(20, 32) foreach { | ||
case (i, j) => | ||
println(s"($i, $j)") | ||
} | ||
def foo(n: Int, v: Int) = | ||
for (i <- 0 until n; | ||
j <- i until n if i + j == v) | ||
yield (i, j) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Again, I'm not sure this is the best style to promote as it invites confusion about the comprehension part and the yield part. You don't indent the for (...)
yield (i, j) or for (
...
)
yield (i, j) There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I agree it looks nicer but all of the examples in Odersky's book use an indent as does IntelliJ. |
||
|
||
foo(10, 10) foreach { | ||
case (i, j) => | ||
print(s"($i, $j) ") // prints (1, 9) (2, 8) (3, 7) (4, 6) (5, 5) | ||
} | ||
``` | ||
|
||
This example shows that comprehensions are not restricted to lists. The previous program uses iterators instead. Every datatype that supports the operations `withFilter`, `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) | ||
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: | ||
``` | ||
|
||
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`: | ||
|
||
(0, 0) (0, 1) (0, 2) (0, 3) (0, 4) (0, 5) (0, 6) (0, 7) (0, 8) (0, 9) (1, 1) ... | ||
``` | ||
object ComprehensionTest3 extends App { | ||
for (i <- Iterator.range(0, 20); | ||
j <- Iterator.range(i, 20) if i + j == 32) | ||
println(s"($i, $j)") | ||
} | ||
``` | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Although the style of indentation in parens varies widely, I'm not sure this one is a good one for a tutorial since we don't do trailing braces in Scala, generally, and trailing parens aren't that different from trailing braces. I'd format it as