for with yield: Loops That Build
The club's website shows distances in miles, so the logbook needs a new list: every run, converted. You could start with an empty list, loop, and append. Scala has a shorter way that returns the new collection directly.
The rule
doperforms;yieldproduces a new collection.
val runs = List(5.0, 8.5, 3.2, 10.0)
val miles = for km <- runs yield km * 0.62
// List(3.1, 5.27, 1.984, 6.2)- A
for … yieldvisits each element just likefor … do, but keeps the value from each pass and gathers them into a new collection. - The result is an expression you can store. The original
runsis untouched. - The kind of collection follows the source: a List in gives a List out, and a Vector gives a Vector. A Range gives an indexed sequence:
for lap <- 1 to 3 yield lap * 400isVector(400, 800, 1200).
Guards and several generators work the same way as with do:
val longMetres = for km <- runs if km >= 5 yield km * 1000
// List(5000.0, 8500.0, 10000.0)
val slots = for name <- List("Asha", "Ben"); day <- List("Sat", "Sun") yield s"$name-$day"
// List(Asha-Sat, Asha-Sun, Ben-Sat, Ben-Sun)This form is called a for comprehension. The compiler rewrites it into collection methods: the first example becomes runs.map(km => km * 0.62), and a guard becomes a filtering step. You will meet map and its relatives directly later, and knowing they are the same thing makes both easier to read.
Compare the loop you might write in another language:
var miles2 = List.empty[Double]
for km <- runs do
miles2 = miles2 :+ km * 0.62It works, but it needs a var, an empty starting value and an append, and each one is a place for mistakes. The yield version has none of them.
The shortcut
When a task says "a new list of…" or "convert each…", reach for yield. When it says "print", "save" or "send" each item, use do.
Generator
km <- runsvisits 5.0, 8.5, 3.2 and 10.0 in turn.
Drills
Answer fast — trust the rule.
What is for km <- List(5.0, 10.0) yield km * 2?
What is for lap <- 1 to 3 yield lap * 400?
With runs = List(5.0, 8.5, 3.2, 10.0), what is for km <- runs if km >= 5 yield km?
What is the difference between for … do and for … yield?
How many elements does for a <- List(1, 2); b <- List(1, 2, 3) yield a * b produce?
Remember
for … yieldgathers each pass's value into a new collection of the same kind as the source.- "Convert each" or "a new list of" means yield; "print" or "save" each means do.
- Trap: building a list with a var and appends when yield does it in one line.
