AmouAI Hub/Courses/Programming Fundamentals/Day 4
Iteration: Loops
The moment a program can repeat itself, it stops being a calculator and starts being software. Today is the biggest single jump in the whole course.
By the end of today you can
- Write
whileloops with a correct initialise / test / update structure - Use
forandrange()in all three forms without off-by-one errors - Apply the accumulator pattern to sum, count, track a maximum, and build a string
- Use
breakandcontinuedeliberately rather than by reflex - Reason about nested loops and how many times the body actually runs
- Build a trace table to debug any loop, on paper or in your head
▶Today's videos
Watch each video, then work the matching sections below. Watching alone will not do it.
1Why loops exist
One idea, then the two ways Python spells it.
Print the numbers 1 to 5. Without a loop:
print(1)
print(2)
print(3)
print(4)
print(5)Now print 1 to 1,000,000. You see the problem. With a loop, both jobs are the same size:
for n in range(1, 6):
print(n)The while loop
A while loop keeps going as long as its condition is true. It has three parts,
and forgetting any one of them is a bug:
count = 1 # 1. INITIALISE - set up before the loop
while count <= 5: # 2. TEST - checked before every pass
print(count)
count += 1 # 3. UPDATE - move toward making the test false
print("Done.")Remove count += 1 and the condition is true forever. Your program hangs. Press Ctrl+C in the terminal to stop it. This will happen to you - it happens to everyone - and recognising it instantly is the skill.
The for loop and range()
Use for when you know how many times, or when you are walking through a sequence.
range(5) # 0, 1, 2, 3, 4 - stop only
range(1, 6) # 1, 2, 3, 4, 5 - start and stop
range(0, 10, 2) # 0, 2, 4, 6, 8 - start, stop, step
range(5, 0, -1) # 5, 4, 3, 2, 1 - counting down
for letter in "cat": # you can loop over a string directly
print(letter)range(1, 6) gives you 1 through 5. This is deliberate: it means range(n) produces exactly n values, and range(len(s)) produces exactly the valid indices of s. It is also the source of roughly every off-by-one error you will write this week.
2The accumulator pattern
If you learn one shape today, learn this one. It is the answer to a startling proportion of programming problems.
The pattern is always the same four steps:
- Initialise an accumulator variable before the loop
- Loop over the data
- Update the accumulator each time round
- Use it after the loop ends
# SUM
total = 0 # identity for addition
for n in range(1, 11):
total += n
print(total) # 55
# COUNT
count = 0
for ch in "programming":
if ch in "aeiou":
count += 1
print(count) # 3
# PRODUCT
product = 1 # identity for multiplication - NOT 0
for n in range(1, 6):
product *= n
print(product) # 120
# MAXIMUM
numbers = [4, 19, 3, 27, 8]
biggest = numbers[0] # start with a real value, not 0
for n in numbers:
if n > biggest:
biggest = n
print(biggest) # 27
# BUILD A STRING
initials = ""
for word in "ada lovelace byron".split():
initials += word[0].upper()
print(initials) # ALBStart a product at 0 and the answer is always 0. Start a maximum at 0 and it breaks the moment all your data is negative. Start it at the first actual value instead. Neither mistake raises an error.
break and continue
# break - leave the loop entirely
for n in range(1, 100):
if n * n > 50:
print(f"First square over 50 is {n}*{n} = {n*n}")
break
# continue - skip the rest of THIS pass, go to the next
for n in range(1, 11):
if n % 2 == 0:
continue # skip evens
print(n, end=" ")The loop else
Unique to Python and genuinely useful: else on a loop runs only if the loop finished
without hitting break. It reads as "if we never found it".
target = 7
for n in [2, 4, 6, 8]:
if n == target:
print("Found it")
break
else:
print("Not in the list")3Nested loops
A loop inside a loop. Simple to write, and the first place where the cost of what you wrote starts to matter.
for row in range(3):
for col in range(4):
print(f"({row},{col})", end=" ")
print() # newline after each rowThe inner loop runs completely for every single pass of the outer loop. Three outer passes times four inner passes is twelve executions of the body.
Patterns
# right triangle
for row in range(1, 6):
print("*" * row)
print()
# pyramid - the width of the padding is the thing to work out
size = 5
for row in range(1, size + 1):
spaces = size - row
stars = 2 * row - 1
print(" " * spaces + "*" * stars)Do not try to see the whole picture at once. Ask: for row number i, how many spaces and how many stars? Write that formula, then wrap it in a loop. Every pattern problem collapses under that question.
4The patterns you will use constantly
Four loop shapes that cover most of what real programs do with repetition.
1. Input validation - keep asking until it is right
while True:
raw = input("Enter an age (0-120): ")
if raw.isdigit() and 0 <= int(raw) <= 120:
age = int(raw)
break
print("That is not a valid age. Try again.")
print(f"Thank you. Age recorded as {age}.")while True with a break is the standard idiom here. It looks alarming and is
completely normal - the exit condition simply lives in the middle rather than at the top.
2. Sentinel - loop until a terminator value
total = 0
while True:
entry = input("Amount (or 0 to finish): ")
value = float(entry)
if value == 0:
break
total += value
print(f"Total: {total:.2f}")3. Search with a flag
numbers = [4, 19, 3, 27, 8]
target = 27
found = False
position = -1
for i in range(len(numbers)):
if numbers[i] == target:
found = True
position = i
break
if found:
print(f"Found {target} at position {position}")
else:
print(f"{target} is not in the list")4. A real algorithm - is it prime?
n = 97
if n < 2:
is_prime = False
else:
is_prime = True
divisor = 2
while divisor * divisor <= n: # only need to check up to the square root
if n % divisor == 0:
is_prime = False
break
divisor += 1
print(f"{n} prime? {is_prime}")If n = a * b and both a and b were larger than √n, their product would exceed n. So at least one factor is always at or below the square root. Checking further is guaranteed to find nothing. For a million-digit check that is the difference between instant and never.
FizzBuzz, and why the order matters
for n in range(1, 16):
if n % 15 == 0: # check the MOST specific case first
print("FizzBuzz")
elif n % 3 == 0:
print("Fizz")
elif n % 5 == 0:
print("Buzz")
else:
print(n)Put the n % 3 test first and 15 prints "Fizz" instead of "FizzBuzz". Same trap as
yesterday's grade calculator: most specific branch first.
5Debugging loops: the trace table
When a loop is wrong, do not stare at it. Build a table.
This is supposed to sum 1 to 5. It produces 10.
total = 0
for i in range(5):
total += i
print(total) # 10, not 15Build the table. One row per iteration, one column per variable, plus what the condition was:
| iteration | i | total before | total after |
|---|---|---|---|
| 1 | 0 | 0 | 0 |
| 2 | 1 | 0 | 1 |
| 3 | 2 | 1 | 3 |
| 4 | 3 | 3 | 6 |
| 5 | 4 | 6 | 10 |
The table makes it obvious: i never reaches 5, and it wasted an iteration on 0. The fix is
range(1, 6). You did not need to be clever - you needed to write down what actually happened.
The five off-by-one signatures
| Symptom | Usual cause |
|---|---|
| Result is short by the last item | range(1, n) where you meant range(1, n+1) |
| Result includes a spurious 0 | range(n) where you meant range(1, n+1) |
IndexError on the final pass | range(len(s) + 1) |
| Loop runs one time too many | while i <= n where you meant < |
| Loop never runs at all | Condition already false at the start, or range(5, 1) with no negative step |
print(total) inside a loop gives you a column of numbers with no context. print(f"i={i} total={total}") gives you the trace table for free. It costs six extra characters and saves twenty minutes.
>_Python playground
A real Python interpreter running inside your browser. Nothing is installed, nothing is uploaded, nothing can break.
input(), comma separated →
✓Exercise set
Checked automatically the moment you submit. Work top to bottom — each one assumes the last. Your answers are saved in this browser.
Day 5 — Debugging, Clean Code & Mini-Project 1
How to find bugs on purpose instead of by accident - then build something real.