5. Loops
Computers never get bored. They’ll happily do the same thing a million times, and that’s where much of their power comes from. In this lesson you’ll learn to write loops: code that runs again and again.
In this lesson you’ll learn:
- how to repeat code with
forand a range like0..5 - how to add things up and count things in a loop
- how to repeat “as long as something is true” with
while - how to leave a loop early with
break, or skip a round withcontinue - how to put a loop inside another loop
- common loop patterns: finding the first match, and finding the largest
- how to recognize and stop a loop that never ends
Repeating with for
Section titled “Repeating with for”Say you want to print five lines. You could write print five times. But what
about a hundred lines? Or a number of lines that depends on what the user
types? A for loop does the repeating for you:
fn main() { for i in 0..5 { print("Hello number {i}") } print("Done!")}Hello number 0Hello number 1Hello number 2Hello number 3Hello number 4Done!Here’s what happens:
0..5is a range: the whole numbers from 0 up to 5, but not including 5. So: 0, 1, 2, 3, 4.- The block between
{and}is the body of the loop. It runs once for each number in the range. Each run is called a round, or an iteration. - In each round,
iholds the current number.iis the loop variable; you can give it any name you like. - When the range is used up, the program carries on after the loop, and prints “Done!”.
Why doesn’t 0..5 include 5?
Section titled “Why doesn’t 0..5 include 5?”It looks odd at first, but it’s handy: 0..5 runs exactly 5 times, and
0..n runs exactly n times. Programmers very often count from 0, as you’ll
see when you meet lists.
To count from 1 instead, start at 1, and end one past the last number you want:
fn main() { for day in 1..8 { print("Day {day}") }}Day 1Day 2Day 3Day 4Day 5Day 6Day 7The ends of a range can be calculations, too. To go from 1 up to and
including n, write 1..(n + 1):
fn main() { let n = 4 for i in 1..(n + 1) { print("{i} squared is {i * i}") }}1 squared is 12 squared is 43 squared is 94 squared is 16Some languages write “up to and including” as 1..=4 or 1...4. Tessel
doesn’t: if you try, it says ranges in Tessel are written `a..b`, and leave out `b` and suggests 1..(n + 1).
If the end isn’t larger than the start, as in 5..0, the range is empty and
the body doesn’t run at all. A for loop can’t count down by itself. To count
down, calculate the number from the loop variable: see exercise 1.
When you don’t need the number at all, name the loop variable _:
fn main() { for _ in 0..3 { print("Hip hip hooray!") }}Hip hip hooray!Hip hip hooray!Hip hip hooray!See for loops and Range in the language guide for more.
Adding things up
Section titled “Adding things up”One of the most common things to do with a loop is to accumulate a result: keep a running total in a variable, and add to it in each round. Here’s the sum of all numbers from 1 to 100:
fn main() { var total = 0 for i in 1..101 { total += i } print("1 + 2 + ... + 100 = {total}")}1 + 2 + ... + 100 = 5050The pattern has three steps:
- Before the loop, create a variable with a starting value (
0for a sum). - Inside the loop, update it (
total += i). - After the loop, use the result.
Counting is the same pattern: add 1 each time something is true. How many numbers from 1 to 100 are divisible by 7?
fn main() { var count = 0 for n in 1..101 { if n % 7 == 0 { count += 1 } } print("{count} numbers from 1 to 100 are divisible by 7.")}14 numbers from 1 to 100 are divisible by 7.The if from lesson 4 sits inside the loop body.
You can put anything in a loop body, including decisions and other loops.
Common mistake: creating the total inside the loop
Section titled “Common mistake: creating the total inside the loop”What’s wrong here?
fn main() { for i in 1..4 { var total = 0 total += i print("total is {total}") }}total is 1total is 2total is 3The total should grow to 1, 3, 6, but it doesn’t. Because var total = 0 is
inside the body, it runs in every round, so total starts again from 0 each
time. The running total must be created before the loop.
Common mistake: changing the loop variable
Section titled “Common mistake: changing the loop variable”The loop variable belongs to the loop. You can read it, but you can’t change it:
fn main() { for i in 0..5 { i = i + 1 print(i) }}error: can't change `i`, which is a loop variable --> loops.tsl:3:9 |3 | i = i + 1 | ^ ::: loops.tsl:2:9 |2 | for i in 0..5 { | - declared here | = help: the loop sets it for each round; to change the number, copy it into a `var` first: `var x = i`Each round, the loop hands you a fresh copy of the next number, so changing
it wouldn’t affect the loop anyway. If you want to print i + 1, just print
it: print(i + 1). Or change the range.
Repeating while something is true: while
Section titled “Repeating while something is true: while”A for loop is great when you know how many rounds you need. Sometimes you
don’t. A while loop keeps going as long as a condition is true:
fn main() { var n = 3 while n > 0 { print(n) n -= 1 } print("Liftoff!")}321Liftoff!Before each round, Tessel checks the condition. If it’s true, the body runs,
and then it checks again. When it’s false, the loop ends. If the condition
is false from the start (say, n starts at 0), the body never runs.
Here’s a question that really needs while: you put 1000 coins in a savings
account that pays 5% interest a year. How many years until you have 2000?
fn main() { var savings = 1000.0 var years = 0 while savings < 2000.0 { savings *= 1.05 years += 1 } print("After {years} years you have {savings.formatted(decimals: 2)} coins.")}After 15 years you have 2078.93 coins.You couldn’t write this with for 0..something, because you don’t know the
number of years in advance. That’s the point of the program.
A simple rule: use for when you’re counting through a range, and while
when you’re waiting for something to happen.
Warning: loops that never end
Section titled “Warning: loops that never end”Something inside a while loop must eventually make the condition false.
If you forget, the loop runs forever. This countdown forgot n -= 1:
fn main() { var n = 3 while n > 0 { print(n) } print("Liftoff!")}3333…and so on, forever. n is always 3, so n > 0 is always true. Tessel can’t
catch this for you: the program is valid, it just never finishes.
This is called an infinite loop, and it happens to every programmer. Don’t
panic. To stop the program, press Ctrl+C in the terminal (on macOS too:
Control, not Cmd). In the Tessel IDE, press Stop. Then look at your loop
and ask: “what changes in each round, and does it bring the condition closer
to false?”
Leaving early: break and continue
Section titled “Leaving early: break and continue”Two statements change how a loop runs:
breakleaves the loop straight away. The program continues after the loop.continueskips the rest of this round and goes on with the next one.
break is useful when you’re searching and have found what you need. What’s
the first number that’s divisible by both 5 and 7?
fn main() { for n in 1..1000 { if n % 7 == 0 && n % 5 == 0 { print("The first number divisible by 5 and 7 is {n}.") break } }}The first number divisible by 5 and 7 is 35.Without break, the loop would go on and print 70, 105 and so on as well.
continue is useful to skip the rounds you’re not interested in. Here are
the numbers from 1 to 10, without the multiples of 3:
fn main() { for n in 1..11 { if n % 3 == 0 { continue } print(n) }}12457810With break, you can also write a loop that decides in the middle when to
stop. while true would run forever on its own; the break ends it:
fn main() { var guess = 1 while true { if guess * guess > 50 { break } guess += 1 } print("{guess} is the first number whose square is above 50.")}8 is the first number whose square is above 50.Loops inside loops
Section titled “Loops inside loops”A loop body can contain another loop. The inner loop then runs completely, from start to end, in every round of the outer loop:
fn main() { for row in 1..4 { for col in 1..4 { print("{row} x {col} = {row * col}") } }}1 x 1 = 11 x 2 = 21 x 3 = 32 x 1 = 22 x 2 = 42 x 3 = 63 x 1 = 33 x 2 = 63 x 3 = 9The outer loop runs 3 times, and each time the inner loop runs 3 times, so the inner body runs 3 × 3 = 9 times.
A multiplication table
Section titled “A multiplication table”To print a real table, each row should be one line. So instead of printing
inside the inner loop, build up the line as a String and print it once the
row is complete. (+= works on strings too: it adds text to the end.)
fn main() { for row in 1..6 { var line = "" for col in 1..6 { line += "{row * col}".padStart(4) } print(line) }} 1 2 3 4 5 2 4 6 8 10 3 6 9 12 15 4 8 12 16 20 5 10 15 20 25"{row * col}".padStart(4) turns the number into text and adds spaces in
front until it’s 4 characters wide, so the columns line up. See
padStart for details.
This is the accumulate pattern again, with a String instead of a number.
Notice where var line = "" is: inside the outer loop, so each row starts
empty, but outside the inner loop, so the row can grow.
Loop patterns
Section titled “Loop patterns”A handful of patterns cover most loops you’ll ever write. You’ve already seen sum and count. Here are two more.
Find the first
Section titled “Find the first”Go through the candidates and stop at the first match. Keep the match in a variable, so you can use it after the loop:
fn main() { var found = -1 for n in 1..100 { if n * n > 500 { found = n break } } if found == -1 { print("No such number below 100.") } else { print("{found} is the first number whose square is above 500.") }}23 is the first number whose square is above 500.-1 is a placeholder that means “nothing found yet”. It works because -1
can never be a real answer here. (Later you’ll meet optionals, a cleaner way
to say “maybe no value”.)
Find the largest
Section titled “Find the largest”Keep the best value so far, and let each new candidate challenge it. You did
this for three numbers in lesson 4; a loop does it for any number. A ball
thrown up in the air is 20 * t - t * t meters high after t seconds. When
is it highest?
fn main() { var bestTime = 0 var bestHeight = 0 for t in 0..21 { let height = 20 * t - t * t if height > bestHeight { bestHeight = height bestTime = t } } print("Highest point: {bestHeight} m after {bestTime} s.")}Highest point: 100 m after 10 s.Common mistake: a total that grows too big
Section titled “Common mistake: a total that grows too big”Loops can produce very large numbers very quickly. The factorial of a number is all the numbers up to it multiplied together: 5! is 1 × 2 × 3 × 4 × 5 = 120. Let’s print a few:
fn main() { var product = 1 for n in 1..30 { product *= n print("{n}! = {product}") }}1! = 12! = 23! = 64! = 245! = 1206! = 7207! = 50408! = 403209! = 36288010! = 362880011! = 3991680012! = 47900160013! = 622702080014! = 8717829120015! = 130767436800016! = 2092278988800017! = 35568742809600018! = 640237370572800019! = 12164510040883200020! = 2432902008176640000error: integer overflow --> loops.tsl:4:921! doesn’t fit in an Int, so the program stops with a runtime error, as you
saw in lesson 3. Notice
also that product starts at 1, not 0: for a product, 1 is the “nothing
yet” value, because multiplying by 0 would give 0 forever.
Worked example: FizzBuzz
Section titled “Worked example: FizzBuzz”FizzBuzz is a counting game, and a classic programming puzzle. Count from 1 to 15, but:
- for multiples of 3, say “Fizz” instead of the number,
- for multiples of 5, say “Buzz”,
- for multiples of both 3 and 5, say “FizzBuzz”.
Break it into steps. First, a loop that counts from 1 to 15: for n in 1..16.
Then, inside it, a decision with four outcomes: an if / else if chain.
Which check comes first? A multiple of both, like 15, is also a multiple of 3.
If “multiple of 3” came first, 15 would say “Fizz” and never reach
“FizzBuzz”. So the most specific check goes first, just like the leap year
rule in lesson 4. A multiple of both 3 and 5 is a multiple of 15:
fn main() { for n in 1..16 { if n % 15 == 0 { print("FizzBuzz") } else if n % 3 == 0 { print("Fizz") } else if n % 5 == 0 { print("Buzz") } else { print(n) } }}12Fizz4BuzzFizz78FizzBuzz11Fizz1314FizzBuzzTry moving the n % 15 check to the end, and see the last line turn into
“Fizz”.
Exercises
Section titled “Exercises”1. Countdown. Use a for loop over 0..10 to print the numbers from 10
down to 1, then “Liftoff!”. Hint: if i goes up, what goes down?
Solution
fn main() { for i in 0..10 { print(10 - i) } print("Liftoff!")}10987654321Liftoff!When i is 0, 10 - i is 10; when i is 9 (the last round), it’s 1.
2. Even sum. Add up all the even numbers from 1 to 100.
Solution
fn main() { var total = 0 for n in 1..101 { if n % 2 == 0 { total += n } } print("The even numbers from 1 to 100 add up to {total}.")}The even numbers from 1 to 100 add up to 2550.3. A triangle of stars. Print this triangle with two nested loops. Row 1 has one star, row 2 has two, and so on:
***************Solution
fn main() { for row in 1..6 { var line = "" for _ in 0..row { line += "*" } print(line) }}The inner range 0..row runs row times, so it adds as many stars as the row
number.
4. Is it prime? A prime number is a whole number of at least 2 that can only be divided evenly by 1 and itself: 2, 3, 5, 7, 11, 13… Check whether a number is prime by trying every divisor from 2 up to (but not including) the number itself. If you find a divisor, print it, and stop searching. Try 91, 97 and 1.
Solution
fn main() { let number = 91 var isPrime = number >= 2 for divisor in 2..number { if number % divisor == 0 { print("{number} = {divisor} x {number / divisor}") isPrime = false break } } if isPrime { print("{number} is prime.") } else { print("{number} is not prime.") }}91 = 7 x 1391 is not prime.With 97 it prints 97 is prime., and with 1, 1 is not prime.. isPrime
starts as number >= 2, because 0 and 1 are not prime; for them, the range
2..number is empty and the loop doesn’t run at all.
5. The 3n + 1 game. Start with a number. If it’s even, halve it; if it’s odd, multiply by 3 and add 1. Repeat until you reach 1. Nobody has ever found a starting number that doesn’t reach 1, but nobody has proved that all of them do, either! Starting from 27, count the steps it takes to reach 1, and find the highest number on the way.
Solution
fn main() { let start = 27 var n = start var steps = 0 var highest = n while n != 1 { if n % 2 == 0 { n = n / 2 } else { n = 3 * n + 1 } steps += 1 highest = max(highest, n) } print("{start} reaches 1 after {steps} steps.") print("On the way, it climbs as high as {highest}.")}27 reaches 1 after 111 steps.On the way, it climbs as high as 9232.This is a job for while: you can’t know the number of steps in advance.
That’s exactly what the program finds out.
Summary
Section titled “Summary”for i in a..b { … }runs the body once for each whole number fromaup to, but not including,b.0..nrunsntimes. Use_when you don’t need the number.- To accumulate (sum, count, build a string), create the variable before the loop, update it inside, and use it after.
while condition { … }repeats as long as the condition is true. Use it when you don’t know the number of rounds in advance.- Make sure something in a
whileloop moves it toward the end. Stop a runaway program with Ctrl+C, or Stop in the IDE. breakleaves a loop;continueskips to the next round.- A loop inside a loop runs completely in every round of the outer loop.
- Useful patterns: sum, count, find the first (with
break), and find the largest (keep the best so far).
See also: Control flow in the language guide.
Next: 6. Functions