I’m back on Scala’s for comprehension construct, courtesy of chapter 2 of Cay Horstmann’s “Scala for the Impatient”. As you’d expect from a book with such a title, he wastes no time in getting to the point, and after a quick (thou comprehensive) intro to the basic single-generator-and-little-else flavour he’s off into multiple generators:
for (i <- 1 to 3; j <- 1 to 3)The generators here are in the first set of parens, and give us the i and j variables which are then used in the body of the loop. All very clear.
print((10 * i + j) + " ") // Prints 11 12 13 21 22 23 31 32 33
from Scala for the Impatient, by Cay Horstmann, pp 20
Then we’re onto the addition of guard conditions:
for (i <- 1 to 3; j <- 1 to 3 if i != j) print((10 * i + j) + " ") // Prints 12 13 21 23 31 32Now before we had “filters” which seemed to do a similar thing – only continue evaluation of that iteration of the loop if they evaluated to true themselves - but filters were applied in the body whereas here the guard condition is found within the generator parens, at least in this example.
from Scala for the Impatient, by Cay Horstmann, pp20
A bit of reading ahead soon reveals that these “filters” and “guard conditions” seem to be the same thing, but things just looked different because there are two valid forms of the for-comprehension syntax – one with parens and semi-colons (Cay’s way in) and the other with curly braces (the Atomic Scala opener). But before we make any sweeping statements, lets check to make sure. Here’s what happens when we start with a parens-version and convert it to the curly-braces flavour:
for (i <- 1 to 3; from = 4 - i; j <- from to 3)
print((10 * i + j) + " ")
// Prints 13 22 23 31 32 33
for { // added a newline and replaced the open-paren with a curly brace
i <- 1 to 3 // added a newline and dropped the semicolon
from = 4 – i // added a newline and dropped the semicolon
j <- from to 3 // added a newline and dropped the semicolon
} // added a newline and replaced the close-paren with a curly brace
print((10 * i + j) + " ")
// Still prints 13 22 23 31 32 33
adapted from Scala for the Impatient, by Cay Horstmann, pp20
As expected, they are the same. I’m not sure if that added flexibility makes me happy or sad. I can however cope with the different ways of referring to filters/guard-conditions. But wait, there’s no time for emotion, yield is about to be introduced.
And here again another subtlety I wasn’t previously aware of seems to have arisen – without a yield, what we have is called a “for-loop”, but once we’re yielding it’s a “for-comprehension”. However, as I am already aware, yielding creates a collection filled with objects of the type output by the first generator, so it’s swings and roundabouts.
Update (08/02/2014): Chris Phelps (one of my Scala-mentors, for which I'm exceedingly grateful) comments: "Underneath the syntactic sugar, the "filter" or "guard" actually uses a variant of the filter method (withFilter), so if you thought those filters were related to the filter method, well spotted." Looks like I'm on the right track.
No comments:
Post a Comment