Repetition
Computers are good at doing the same thing many times without getting bored or making a mistake on the four-hundredth try. A loop runs a block of code again and again. This chapter counts, adds up, builds tables and draws rows of boxes, each with a few lines of loop.
Counting with for
for (let i = 1; i <= 5; i++) {
preview.append(`${i} `)
}
A for loop has three parts in its parentheses, separated by semicolons:
let i = 1runs once, before anything else: it makes a counter calledi.i <= 5is checked before each round: while it is true, the block runs again.i++runs after each round: it adds one toi. (i++is short fori = i + 1.)
So the block runs with i set to 1, 2, 3, 4 and 5, and then i <= 5 is false and the loop
stops. Change the 5 to 20, or start i at 10.
Adding things up
A loop that builds an answer usually keeps a running total in a let outside it:
function sumTo(n: 0) {
let total = 0
for (let i = 1; i <= n; i++) {
total = total + i
}
return total
}
preview.append(`1 + 2 + ... + 100 = ${sumTo(100)}`)
test 'adding up' {
expect(sumTo(4)).toBe(10)
expect(sumTo(100)).toBe(5050)
expect(sumTo(0)).toBe(0)
}
sumTo(0) is 0 because the loop never runs at all: 1 <= 0 is false from the start. A loop
that may run zero times is normal, and worth a test.
Building a table
The block of a loop can do anything a program can do, including building text. Here a loop builds a times table, one line per round:
import { elements } from 'tosijs'
const { pre } = elements
function timesTable(n: 0) {
let lines = ''
for (let i = 1; i <= 10; i++) {
lines = lines + `${i} x ${n} = ${i * n}\n`
}
return lines
}
preview.append(pre(timesTable(7)))
test 'the seven times table' {
expect(timesTable(7).startsWith('1 x 7 = 7\n')).toBe(true)
expect(timesTable(7).includes('10 x 7 = 70')).toBe(true)
}
\n inside a string means "start a new line", and pre shows text with its line breaks kept.
Drawing many things
Loops are how a program draws a row of anything. This one makes boxes that grow:
import { elements } from 'tosijs'
const { div } = elements
for (let i = 1; i <= 6; i++) {
preview.append(
div({
style: {
display: 'inline-block',
width: `${i * 10}px`,
height: `${i * 10}px`,
margin: '4px',
background: 'teal',
},
})
)
}
Try changing teal to orange, 6 to 12, or i * 10 to i * i.
while: until something changes
for is for counting. When you do not know how many rounds it will take, while repeats as
long as its condition is true:
function halvings(n: 0.0) {
let steps = 0
while (n > 1) {
n = n / 2
steps = steps + 1
}
return steps
}
preview.append(`Halving 100 until it is 1 or less takes ${halvings(100)} steps.`)
test 'halving' {
expect(halvings(100)).toBe(7)
expect(halvings(1)).toBe(0)
}
Every while needs something inside it that eventually makes its condition false; here,
n keeps shrinking. A loop whose condition can never become false runs forever, and the page
stops responding until you reload it. If that happens, it is not your computer: check what
your loop changes each round.
Try it
Write countMultiples(n: 0, of: 0) that counts how many numbers from 1 to n divide evenly
by of (remember % from Decisions). Test that countMultiples(10, 3) is 3 (3, 6 and 9).
Then show the classic: for 1 to 15, show 'fizz' for multiples of 3, 'buzz' for multiples
of 5, 'fizzbuzz' for both, and the number otherwise.