Loops and ranges
Repeating work with for-in, counting with ranges, and looping while a condition still holds.
Repeat without copying
A for in loop walks through a collection one item at a time. You name the item, and the block between the braces runs once for each value.
This is how you write one line that handles a list of any length. Copying the same line ten times would break the moment the list changed size.
The loop variable is a copy of the item, not a way to change the collection. Assigning to name inside the block changes only that pass; the array keeps its original values. When a loop must change the array, iterate over its indices or build a new array instead, because changing a collection while walking it is the source of many confusing results.
Counting with ranges
A range gives you the numbers themselves. 1...3 counts one, two, three, with both ends included. 1..<3 counts one and two, leaving the last number out.
When the number in the range is not needed, name it with a single underscore so the reader knows the value is deliberately ignored.
A range is a value in its own right, so 1...3 can be stored and passed around. reversed() turns one around without changing it, and stride handles a step that is not one. for i in (1...3).reversed() counts three, two, one, which is clearer than subtracting from a bound inside the block.
while, and doing it once at least
A while loop repeats while a condition stays true, which is the right tool when you do not know the count in advance. Something inside the block must move the condition toward false, or the loop never ends.
The repeat form runs the block first and checks the condition after. That is how you guarantee the body runs at least once.
The condition of a while is checked before every pass, including the first. If it is false at the start, the body never runs at all. A while true loop therefore needs a break somewhere inside; the compiler will not warn you, because a loop that leaves through a break is perfectly legal.
Three kinds of loop
let names = ["Ada", "Alan"]
for name in names {
print("Hello,", name)
}
for i in 1...3 {
print("Step", i)
}
var n = 3
while n > 0 {
print("Left", n)
n -= 1
}
repeat {
print("runs at least once")
} while falseStride, continue and labels
stride(from:to:by:) walks numbers by a step other than one: stride(from: 10, to: 0, by: -2) counts down in twos, and the to value is excluded. through: includes the final value. It is the tool for every count that is not a simple range.
continue skips the rest of the current pass, and break leaves the loop. A label before the loop lets an inner break leave the outer one too: outer: for … { for … { break outer } }. Labels are rare, but they are clearer than a flag when a nested search is done.
continue does not skip the loop’s own bookkeeping. In a for loop it jumps to the next item, and in a while loop it jumps back to the condition, so any update below the continue is skipped. That is the usual bug: a counter incremented at the bottom of the block never moves when the skipped branch is taken.
Three dots (1...3) includes three; two dots and a less-than (1..<3) stops at two. Mixing them up is the usual off-by-one. A while loop needs the same care: something inside must move the condition toward false.
Loops are not the only tool
Swift’s collections also answer questions directly: map transforms every item, filter keeps the ones that pass, and reduce folds them into one value. A loop is still the right tool when the work is a sequence of side effects, but when the goal is a new collection, the method says it in one line and removes the index arithmetic.
Use the method when you want a new value; use the loop when each pass must do something to the outside world.
Chained methods read from left to right, but each one produces a whole new array before the next starts. For a list of a few dozen scores that is irrelevant; for a very large list it is worth knowing that a loop with one accumulator can do the same work without the intermediate copies.
Try these next
- Print every other number from 1 to 9 with
stride(from: 1, through: 9, by: 2). - Use
continueto skip one name in the greeting loop. - Rewrite the countdown with
strideand nowhile.
Greetings, steps and a countdown
Run it and follow the order of the lines. Then change the countdown start to five and predict how many count lines print.
A stride and a labelled break
Run it, then change the stride step to -3 and predict the countdown before running it again.
A loop that never ends is the most common mistake here. Check that every value used in the condition changes somewhere inside the block, so the loop can reach its end.
Check what you learned
One question at a time. A wrong answer explains itself — take another go.
Part 1 of 5
What does for name in names do?
