Pebellslearn by building
0%
RoadmapsMatrixCoursesSandboxSign in
Loops  ›  Capstone Project

for with yield: Loops That Build

for comprehension 14 minA for with yield builds and returns a new collection of the same kind as its source from the value of each pass, turning a loop into an expression.

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

do performs; yield produces 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 … yield visits each element just like for … 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 runs is 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 * 400 is Vector(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.62

It 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.

Long runs in metres, built with yieldStep 1 of 4
  1. Generator

    km <- runs visits 5.0, 8.5, 3.2 and 10.0 in turn.

Drills

Answer fast — trust the rule.

quick drill

What is for km <- List(5.0, 10.0) yield km * 2?

quick drill

What is for lap <- 1 to 3 yield lap * 400?

quick drill

With runs = List(5.0, 8.5, 3.2, 10.0), what is for km <- runs if km >= 5 yield km?

quick drill

What is the difference between for … do and for … yield?

quick drill

How many elements does for a <- List(1, 2); b <- List(1, 2, 3) yield a * b produce?

Remember

  • for … yield gathers 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.

Practice — level up

0/1 solved

Write your answer, then compare it with a rubric and a model answer.

ExplainFrom append loop to yield

A teammate builds a list of long runs in metres with var result = List.empty[Double] and a for … do loop that appends km * 1000 for runs of at least 5 km. Rewrite it with yield, give the result for Asha's runs, and explain why the yield version is better.