Pre-COSMOS Day 8, for Cluster 10: Robot Inventors, a 60-minute robotics upgrade. It teaches for loops as the known-repeat scanning loops a robot uses: processing each sensor reading, repeating exactly N times with range(), scanning every other position with range(start, stop, step), counting obstacles, and using nested loops to scan a 3x5 camera image row by row. The watch-outs around range() excluding its end value appear both as trap slides and as checks. Every snippet was executed under CPython 3.12.4.
Subject: Python · 91 slides · code lesson
Open the interactive version of this deck · Homework for this lesson
Objectives
Yesterday, while meant: keep going until a condition changes. Today, for means: repeat for each item, or repeat exactly N times.
1. Use for item in list to process robot sensor readings one at a time.
2. Use range(n) for exactly N repeats, and explain why range(5) stops before 5.
3. Use range(start, stop, step) to scan patterns like every other pixel.
4. Count and record results with counters, indexes, and enumerate().
5. Use nested loops to scan a 3x5 camera grid row by row, then decide when a robot behavior needs for instead of while.
Warm-up
Discussion prompt
Before we open for Loops as Robot Scanning Loops: without looking back, what was the main idea of Lists: Operations & Slicing, and what could you do by the end of it that you could not do before?
Hint: One sentence for the idea, one for the skill. If the second one is blank, that is the part to revisit.
Answer:
An in-depth session of 41 slides. It covers indexing from 0, negative indexes, and slicing with the end excluded, along with the slice shortcuts, joining with + and repeating with *, changing items, and append. The three misses the diagnostic found each appear as a trap slide, and there are five checks, followed by a scaffolded build-it-yourself Dream Team Roster project. It was built for the Pre-COSMOS Day 3 hour.
Concept
Robots repeat tiny actions constantly: take five readings, test each sensor value, scan a row of pixels, or try the same movement a fixed number of times.
| robot task | loop idea |
|---|---|
| Check each distance reading | for reading in readings |
| Take 5 test steps | for step in range(5) |
| Scan columns 1, 3, 5, 7, 9 | range(1, 10, 2) |
| Scan a camera image | nested loops: rows, then columns |
By the end, the range traps that were almost solid on the diagnostic should feel boring in the best way.
Comparison
Comparison matrix
From Why this matters for robots: refill the loop idea column from what you know. The rest of the table is as it appeared.
| robot task | loop idea |
|---|---|
| Check each distance reading | for reading in readings |
| Take 5 test steps | for step in range(5) |
| Scan columns 1, 3, 5, 7, 9 | range(1, 10, 2) |
| Scan a camera image | nested loops: rows, then columns |
Concept
while is for an unknown number of repeats: keep driving until the robot reaches the goal, or keep asking until a condition changes.
for is for a known set of repeats: one pass for each item in a list, or exactly N passes from range().
| question | usually use |
|---|---|
| Do I already know the items or count? | for |
| Am I waiting for the world to change? | while |
| Do I need one pass per sensor reading? | for |
| Do I need to stop when the goal is reached? | while |
Discrimination
Sort into buckets
Sort these by usually use, from memory, without looking back at For vs while. Telling them apart on the spot is the skill; the table is only where the answer happens to be written down.
forwhilefor" for Do I already know the items or count?, Do I need one pass per sensor reading? — that is what the table on "For vs while" records, and it is the single property separating this group from the rest.while" for Am I waiting for the world to change?, Do I need to stop when the goal is reached? — that is what the table on "For vs while" records, and it is the single property separating this group from the rest.Concept
A for loop has a loop variable, a sequence to walk through, a colon, and an indented body.
| piece | job |
|---|---|
for | starts the loop |
distance | loop variable: the current item |
in readings | the sequence being scanned |
: | says the indented body starts next |
| indented lines | the work repeated once per item |
Indentation decides what repeats. A line lined up under the for runs every pass; a line back at the left runs after the loop is done.
Trade off
Comparison matrix
From The shape of a for loop: every row here is a choice with a cost. Fill the job column, then say which row you would actually pick and what you give up for it.
| piece | job |
|---|---|
for | starts the loop |
distance | loop variable: the current item |
in readings | the sequence being scanned |
: | says the indented body starts next |
| indented lines | the work repeated once per item |
Ranking
Put in order
Put the moves of Warm-up: five robot readings into the order they have to happen.
Why: These are the moves of the worked example in the order it makes them, and each one is set up by the one before it. The values are 0, 1, 2, 3, and 4.
Worked example
First, make the robot print five scan numbers. Predict the last number before you reveal the trace.
for step in range(5):
print(step)range(5) gives exactly five values
Why: The values are 0, 1, 2, 3, and 4. The stop number is the first value left out.
The body runs once per value
Why: Line 2 runs five times because range(5) handed over five values.
Trace the output
Why: Verified by execution under CPython 3.12.4.
| pass | step | printed |
|---|---|---|
| 1 | 0 | 0 |
| 2 | 1 | 1 |
| 3 | 2 | 2 |
| 4 | 3 | 3 |
| 5 | 4 | 4 |
Pattern
Step through it
Step through Warm-up: five robot readings one row at a time. What is driving the change, and what would the row after the last one be?
Anomaly
Predict first
A student writes this, and it looks reasonable:
Goal: label robot steps 1 through 5.
It is wrong. Say what breaks — and say it before you turn the page.
Correct: This feels natural, but range(5) means five values starting at 0.
Same goal: label robot steps 1 through 5.
Why: This feels natural, but range(5) means five values starting at 0.
Trap
Goal: label robot steps 1 through 5.
Use range(5) because the goal mentions 5
Why: This feels natural, but range(5) means five values starting at 0.
| wrong output |
|---|
| Step 0 |
| Step 1 |
| Step 2 |
| Step 3 |
| Step 4 |
Same goal: label robot steps 1 through 5.
Use range(1, 6)
Why: Start at 1, and make the stop one past the last label you want.
| right output |
|---|
| Step 1 |
| Step 2 |
| Step 3 |
| Step 4 |
| Step 5 |
Intuition
Think of range(5) as five lockers numbered 0, 1, 2, 3, 4. The 5 is the wall after the last locker.
This is the same rule as slicing: the end is excluded. Python likes starts that are included and stops that are left out.
end excluded — The stop value in range() is not produced. range(5) has 5 values, but none of them is 5.
Counterexample
Discussion prompt
Think of range(5) as five lockers numbered 0, 1, 2, 3, 4. The 5 is the wall after the last locker.
That is stated as though it always holds. Do one of two things: produce a case where it fails, or say precisely what rules such a case out. "It just does" is not on the menu.
Hint: Hunt at the extremes first — zero, one, negative, empty, equal. If every extreme survives, the reason they survive is the proof.
Concept
When you already have readings, loop through the list itself. The loop variable becomes one reading at a time.
| list | loop variable values |
|---|---|
[12, 8, 3, 15, 4] | 12, then 8, then 3, then 15, then 4 |
['r', 'g', 'b'] | 'r', then 'g', then 'b' |
The loop variable is not magic. Name it for what one item means: distance, color, pixel, row, or reading.
Analogy
Discussion prompt
Explain Looping through a list by analogy to something with no Python in it at all — a queue, a recipe, a map, a bank balance, whatever fits. Then say where your analogy breaks.
Hint: An analogy that never breaks is not an analogy, it is the same idea wearing a hat. Find the seam — that is the part that is actually new.
Answer:
When you already have readings, loop through the list itself. The loop variable becomes one reading at a time.
Step zero
Discussion prompt
Process sensor readings — before any calculation: what is the plan? Name the moves in order, in plain English, without doing the arithmetic.
Hint: It starts with: Line 3 picks one reading at a time
Answer:
Worked example
The robot says a reading below 5 means an obstacle is close.
readings = [12, 8, 3, 15, 4]
for distance in readings:
if distance < 5:
print('Obstacle!')
else:
print('Clear')Line 3 picks one reading at a time
Why: distance is 12 on the first pass, 8 on the second pass, and so on.
Lines 4-7 make one decision per reading
Why: The if runs inside the loop body, so every reading gets checked.
Trace each reading
Why: Verified by execution: two readings are close enough to report an obstacle.
| pass | distance | distance < 5? | printed |
|---|---|---|---|
| 1 | 12 | False | Clear |
| 2 | 8 | False | Clear |
| 3 | 3 | True | Obstacle! |
| 4 | 15 | False | Clear |
| 5 | 4 | True | Obstacle! |
Error analysis
Annotate
Walk the callouts on Process sensor readings. Each one is a place this is easy to get subtly wrong.
distance is 12 on the first pass, 8 on the second pass, and so on.if runs inside the loop body, so every reading gets checked.Intuition
Picture the robot handing you one sensor reading on a clipboard. You do the body of the loop with that reading.
On the next pass, the old value is replaced by the next one. You do not move the loop variable forward yourself; the for loop does that for you.
That is why for distance in readings reads almost like English: for each distance in readings, run this body.
Explain it
Discussion prompt
Explain The loop variable is a clipboard to a student a year behind you. No notation, no jargon they have not met — and it still has to be true.
Hint: If your explanation needs a symbol they have never seen, you are describing the notation rather than the idea.
Answer:
Picture the robot handing you one sensor reading on a clipboard. You do the body of the loop with that reading.
Anomaly
Predict first
A student writes this, and it looks reasonable:
Goal: check every reading for an obstacle.
It is wrong. Say what breaks — and say it before you turn the page.
Correct: This checks the first sensor value and then stops.
Same goal: check every reading for an obstacle.
Why: This checks the first sensor value and then stops. The close readings later in the list are never seen.
Trap
Goal: check every reading for an obstacle.
Only test readings[0]
Why: This checks the first sensor value and then stops. The close readings later in the list are never seen.
| checked value | printed |
|---|---|
| 12 | Clear |
Same goal: check every reading for an obstacle.
Loop with for distance in readings
Why: The body runs once for each item, so no reading gets skipped.
| checked values | obstacles reported |
|---|---|
| 12, 8, 3, 15, 4 | 2 |
Break the constraint
Discussion prompt
The rule this trap just fixed:
The body runs once for each item, so no reading gets skipped.
Now break it on purpose. Build a case that violates it and follow the consequences until something visibly fails. Where does the failure first show up — and would you have noticed it if you had not been looking?
Hint: The dangerous rules are the ones whose violation still produces an answer. If yours fails loudly, try to find one that fails quietly.
Answer:
This checks the first sensor value and then stops. The close readings later in the list are never seen.
Estimation
Predict first
A for loop works the same way for colors, names, readings, or pixels: one item per pass.
Commit before you compute: what does A tiny color scan come out to? A rough magnitude and the right form is enough — the point is to have something concrete to be wrong about.
Correct: The list has three items
Why: A prediction you can defend turns the computation into a check rather than a leap of faith — and an answer that contradicts it is caught on the spot. So the loop body runs exactly three times.
Worked example
A for loop works the same way for colors, names, readings, or pixels: one item per pass.
for c in ['r', 'g', 'b']:
print(c)The list has three items
Why: So the loop body runs exactly three times.
Trace the color variable
Why: Verified by execution.
| pass | c | printed |
|---|---|---|
| 1 | 'r' | r |
| 2 | 'g' | g |
| 3 | 'b' | b |
Reverse engineer
Discussion prompt
Work backwards. The example finished here:
Trace the color variable
What was it asked to do, and what must it have been given? Reconstruct the problem from its answer.
Hint: Every quantity in the result had to enter somewhere. Account for each one.
Answer:
A for loop works the same way for colors, names, readings, or pixels: one item per pass.
Concept
range() can take three inputs: where to start, where to stop, and how big each jump should be.
| form | meaning |
|---|---|
range(5) | start at 0, stop before 5, step by 1 |
range(1, 10) | start at 1, stop before 10, step by 1 |
range(1, 10, 2) | start at 1, stop before 10, step by 2 |
Robot meaning: use the step to scan every second column, every tenth sensor, or every other test position.
Step zero
Discussion prompt
Scan every other position — before any calculation: what is the plan? Name the moves in order, in plain English, without doing the arithmetic.
Hint: It starts with: Start at 1
Answer:
Worked example
This scan starts at column 1, jumps by 2, and stops before 10.
for col in range(1, 10, 2):
print(col)Start at 1
Why: The first value produced by the range is the start value.
Jump by 2 until the next jump would reach or pass 10
Why: The produced values are 1, 3, 5, 7, and 9. The stop value 10 is not included.
Trace the scan
Why: Verified by execution.
| pass | col | printed |
|---|---|---|
| 1 | 1 | 1 |
| 2 | 3 | 3 |
| 3 | 5 | 5 |
| 4 | 7 | 7 |
| 5 | 9 | 9 |
Scale up
Step through it
Step through Scan every other position and watch the numbers move. Now imagine the input ten times bigger: which column is the one that stops this being practical?
Anomaly
Predict first
A student writes this, and it looks reasonable:
Goal: scan odd columns 1, 3, 5, 7, 9.
It is wrong. Say what breaks — and say it before you turn the page.
Correct: This looks like it should end at 9, but 9 is the stop value, so it is excluded.
Same goal: scan odd columns 1, 3, 5, 7, 9.
Why: This looks like it should end at 9, but 9 is the stop value, so it is excluded.
Trap
Goal: scan odd columns 1, 3, 5, 7, 9.
Use range(1, 9, 2)
Why: This looks like it should end at 9, but 9 is the stop value, so it is excluded.
| wrong output |
|---|
| 1 |
| 3 |
| 5 |
| 7 |
Same goal: scan odd columns 1, 3, 5, 7, 9.
Use range(1, 10, 2)
Why: The stop has to be one past the last odd column you want.
| right output |
|---|
| 1 |
| 3 |
| 5 |
| 7 |
| 9 |
Concept
Use range(n) when the count matters more than the values. range(5) means exactly five passes.
The variable can still be useful for labels, logs, and trace tables. If you truly do not need it, many Python programmers name it _.
| goal | loop shape |
|---|---|
| take 5 readings | for reading_number in range(5) |
| blink 3 times | for blink in range(3) |
| repeat a test 10 times | for trial in range(10) |
Missing information
Discussion prompt
A movement test might need exactly five forward moves, whether or not anything interesting happens.
What do you need to know — or decide — before the first line can be written? List everything the problem has to hand you.
Hint: Anything you would have to invent to get started is a thing the problem must supply.
Answer:
The labels are 0 through 4, but the count of passes is 5.
Worked example
A movement test might need exactly five forward moves, whether or not anything interesting happens.
for step in range(5):
print('move forward', step)The loop repeats exactly five times
Why: The labels are 0 through 4, but the count of passes is 5.
Trace the movement labels
Why: Verified by execution.
| pass | step | printed |
|---|---|---|
| 1 | 0 | move forward 0 |
| 2 | 1 | move forward 1 |
| 3 | 2 | move forward 2 |
| 4 | 3 | move forward 3 |
| 5 | 4 | move forward 4 |
Pattern
Step through it
Step through Five fixed robot moves one row at a time. What is driving the change, and what would the row after the last one be?
Concept
A loop can do more than print. It can keep a counter that survives from one pass to the next.
counter — A variable that starts before the loop and changes inside the loop, often by adding 1 when something is found.
| where | why |
|---|---|
| before the loop | create the counter once |
| inside the loop | update it for each item |
| after the loop | use the final answer |
Step zero
Discussion prompt
Count close readings — before any calculation: what is the plan? Name the moves in order, in plain English, without doing the arithmetic.
Hint: It starts with: Initialize before the loop
Answer:
Worked example
Now count how many readings report a close obstacle.
readings = [12, 8, 3, 15, 4]
obstacles = 0
for distance in readings:
if distance < 5:
obstacles = obstacles + 1
print(obstacles)Initialize before the loop
Why: obstacles = 0 runs once. The robot has not found anything yet.
Add 1 only for close readings
Why: The counter changes on passes where distance < 5 is True.
Trace every pass
Why: Verified by execution: readings 3 and 4 are the two close readings.
| pass | distance | obstacles before | obstacles after |
|---|---|---|---|
| start | - | - | 0 |
| 1 | 12 | 0 | 0 |
| 2 | 8 | 0 | 0 |
| 3 | 3 | 0 | 1 |
| 4 | 15 | 1 | 1 |
| 5 | 4 | 1 | 2 |
Print after the loop
Why: The final output is 2, because the loop has finished scanning all five readings.
Blank canvas
Draw it
Draw what Count close readings just did — the shape of it, not the line-by-line working. One picture, labels only where you need them. Then check it against the steps: anything you could not draw is a step you followed rather than understood.
Anomaly
Predict first
A student writes this, and it looks reasonable:
Goal: count all close readings in [12, 8, 3, 15, 4].
It is wrong. Say what breaks — and say it before you turn the page.
Correct: This erases the count at the start of every pass.
Same goal: count all close readings in [12, 8, 3, 15, 4].
Why: This erases the count at the start of every pass. The robot forgets what it found earlier.
Trap
Goal: count all close readings in [12, 8, 3, 15, 4].
Put obstacles = 0 inside the loop
Why: This erases the count at the start of every pass. The robot forgets what it found earlier.
| pass | distance | counter after pass |
|---|---|---|
| 1 | 12 | 0 |
| 2 | 8 | 0 |
| 3 | 3 | 1 |
| 4 | 15 | 0 |
| 5 | 4 | 1 |
| printed | - | 1 |
Same goal: count all close readings in [12, 8, 3, 15, 4].
Put obstacles = 0 before the loop
Why: The counter is created once, then it can remember across all passes.
| pass | distance | counter after pass |
|---|---|---|
| 1 | 12 | 0 |
| 2 | 8 | 0 |
| 3 | 3 | 1 |
| 4 | 15 | 1 |
| 5 | 4 | 2 |
| printed | - | 2 |
Two truths and a lie
Sort into buckets
Some of these hold up and some are the exact mistakes this lesson is built to prevent. Sort them.
while is for an unknown number of repeats: keep driving until the robot reaches the goal, or keep asking until a condition changes.; A for loop has a loop variable, a sequence to walk through, a colon, and an indented body.Concept
Sometimes the item is enough: for distance in readings. Sometimes the position matters too: which sensor number saw the obstacle?
| need | good loop |
|---|---|
| just the value | for distance in readings |
| index only | for i in range(len(readings)) |
| index and value | for i, distance in enumerate(readings) |
Use indexes when you must look up by position, label coordinates, or compare neighboring items.
Ranking
Put in order
Put the moves of Index scan with range(len()) into the order they have to happen.
Why: These are the moves of the worked example in the order it makes them, and each one is set up by the one before it. The loop variable i is 0, 1, 2, 3, and 4.
Worked example
len(readings) is 5, so range(len(readings)) gives the indexes 0 through 4.
readings = [12, 8, 3, 15, 4]
for i in range(len(readings)):
print(i, readings[i])Line 3 loops through indexes
Why: The loop variable i is 0, 1, 2, 3, and 4.
Line 4 uses the index to fetch the reading
Why: readings[i] means the item at that position.
Trace the positions
Why: Verified by execution.
| pass | i | readings[i] | printed |
|---|---|---|---|
| 1 | 0 | 12 | 0 12 |
| 2 | 1 | 8 | 1 8 |
| 3 | 2 | 3 | 2 3 |
| 4 | 3 | 15 | 3 15 |
| 5 | 4 | 4 | 4 4 |
Comparison
Comparison matrix
From Index scan with range(len()): refill the readings[i] column from what you know. The rest of the table is as it appeared.
| pass | i | readings[i] | printed |
|---|---|---|---|
| 1 | 0 | 12 | 0 12 |
| 2 | 1 | 8 | 1 8 |
| 3 | 2 | 3 | 2 3 |
| 4 | 3 | 15 | 3 15 |
| 5 | 4 | 4 | 4 4 |
Pattern
Predict first
The table runs: 1 | 0 | 12 | 0 12 · 2 | 1 | 8 | 1 8 · 3 | 2 | 3 | 2 3 · 4 | 3 | 15 | 3 15
In Index and value with enumerate, given the rows so far: what is the next one — the row where pass is 5?
Correct: 5 | 4 | 4 | 4 4
| pass | i | distance | printed |
|---|---|---|---|
| 1 | 0 | 12 | 0 12 |
| 2 | 1 | 8 | 1 8 |
| 3 | 2 | 3 | 2 3 |
| 4 | 3 | 15 | 3 15 |
| 5 | 4 | 4 | 4 4 |
Why: The relationship between the columns, not the individual numbers, is what generates the next row. i gets the position, and distance gets the reading at that position.
Worked example
enumerate(readings) hands you both pieces at once: the index and the value.
readings = [12, 8, 3, 15, 4]
for i, distance in enumerate(readings):
print(i, distance)The loop receives a pair each pass
Why: i gets the position, and distance gets the reading at that position.
Trace the same output
Why: This prints the same pairs as the range(len()) version, but the code says the intent more directly.
| pass | i | distance | printed |
|---|---|---|---|
| 1 | 0 | 12 | 0 12 |
| 2 | 1 | 8 | 1 8 |
| 3 | 2 | 3 | 2 3 |
| 4 | 3 | 15 | 3 15 |
| 5 | 4 | 4 | 4 4 |
Concept
These are not the main build today, but they should not surprise you when you see them in examples.
| tool | meaning |
|---|---|
break | stop the loop early |
continue | skip the rest of this pass and go to the next one |
_ | loop variable name used when the value is ignored |
| looping through a string | one character per pass, like pixels in a row |
| changing the list while looping | avoid this as a beginner; it can skip or duplicate work |
Today, your safest default is simple: pick the right sequence, trace one row per pass, and keep changes inside the body easy to see.
Concept
A tiny robot camera image can be stored as a list of rows. Each row is a list of pixels.
| row | 0 | 1 | 2 | 3 | 4 |
|---|---|---|---|---|---|
| 0 | . | . | # | . | . |
| 1 | . | # | # | # | . |
| 2 | . | . | # | . | . |
# means obstacle pixel. . means clear pixel. A 3-row, 5-column image has 15 total positions.
Explain it
Discussion prompt
Explain Camera images are grids to a student a year behind you. No notation, no jargon they have not met — and it still has to be true.
Hint: If your explanation needs a symbol they have never seen, you are describing the notation rather than the idea.
Answer:
A tiny robot camera image can be stored as a list of rows. Each row is a list of pixels.
Intuition
A nested loop is a loop inside another loop. The outside loop chooses the row; the inside loop scans across the columns in that row.
| outside row | inside col values |
|---|---|
| 0 | 0, 1, 2, 3, 4 |
| 1 | 0, 1, 2, 3, 4 |
| 2 | 0, 1, 2, 3, 4 |
That is how a camera scan becomes 15 visits: 3 rows times 5 columns.
Invariant
Step through it
Step through Nested loops scan row by row one row at a time. One of these columns never changes — find it, and say why it cannot.
Step zero
Discussion prompt
Trace a full grid scan — before any calculation: what is the plan? Name the moves in order, in plain English, without doing the arithmetic.
Hint: It starts with: The outer loop chooses a row
Answer:
Worked example
This is the stretch scan from the lesson: print the row, column, and pixel at every position.
image = [
['.', '.', '#', '.', '.'],
['.', '#', '#', '#', '.'],
['.', '.', '#', '.', '.'],
]
for row in range(3):
for col in range(5):
print(row, col, image[row][col])The outer loop chooses a row
Why: row becomes 0, then 1, then 2.
The inner loop scans all five columns for that row
Why: For each row, col becomes 0, 1, 2, 3, and 4.
Trace all 15 positions
Why: Verified by execution. The scan goes left to right across row 0, then row 1, then row 2.
| # | row | col | pixel |
|---|---|---|---|
| 1 | 0 | 0 | . |
| 2 | 0 | 1 | . |
| 3 | 0 | 2 | # |
| 4 | 0 | 3 | . |
| 5 | 0 | 4 | . |
| 6 | 1 | 0 | . |
| 7 | 1 | 1 | # |
| 8 | 1 | 2 | # |
| 9 | 1 | 3 | # |
| 10 | 1 | 4 | . |
| 11 | 2 | 0 | . |
| 12 | 2 | 1 | . |
| 13 | 2 | 2 | # |
| 14 | 2 | 3 | . |
| 15 | 2 | 4 | . |
Concept
Now you build the scanner yourself. Type each milestone before revealing the solution, run after every small change, and read errors before editing.
| requirement | tool you will use |
|---|---|
| print each row of the image | for row in image |
| count obstacle pixels | counter before the loop, update inside |
| print obstacle coordinates | nested row and col loops |
| prove the scan covers 15 positions | second counter plus 3x5 nested loops |
Build rule: do not skip to the final program. The useful skill is making one small loop work, then adding the next one.
Analogy
Discussion prompt
Explain Build: robot camera scanner by analogy to something with no Python in it at all — a queue, a recipe, a map, a bank balance, whatever fits. Then say where your analogy breaks.
Hint: An analogy that never breaks is not an analogy, it is the same idea wearing a hat. Find the seam — that is the part that is actually new.
Answer:
Build rule: do not skip to the final program. The useful skill is making one small loop work, then adding the next one.
Estimation
Predict first
Your turn: create the image list and print each row. Before running, say out loud how many rows should print.
Commit before you compute: what does Your turn 1: print each row come out to? A rough magnitude and the right form is enough — the point is to have something concrete to be wrong about.
Correct: Expected output
Why: A prediction you can defend turns the computation into a check rather than a leap of faith — and an answer that contradicts it is caught on the spot. Three rows print, and each row has five pixels.
Worked example
Your turn: create the image list and print each row. Before running, say out loud how many rows should print.
Hint
Why: The sequence is image, and each item in it is one row.
image = [
['.', '.', '#', '.', '.'],
['.', '#', '#', '#', '.'],
['.', '.', '#', '.', '.'],
]
for row in image:
print(row)Expected output
Why: Three rows print, and each row has five pixels.
| line | output |
|---|---|
| 1 | ['.', '.', '#', '.', '.'] |
| 2 | ['.', '#', '#', '#', '.'] |
| 3 | ['.', '.', '#', '.', '.'] |
Blank canvas
Draw it
Draw what Your turn 1: print each row just did — the shape of it, not the line-by-line working. One picture, labels only where you need them. Then check it against the steps: anything you could not draw is a step you followed rather than understood.
Pattern
Predict first
The table runs: 0 | 1 | 1 · 1 | 3 | 4 · 2 | 1 | 5
In Your turn 2: count obstacle pixels, given the rows so far: what is the next one — the row where row is printed?
Correct: printed | - | 5
| row | obstacles in row | running total |
|---|---|---|
| 0 | 1 | 1 |
| 1 | 3 | 4 |
| 2 | 1 | 5 |
| printed | - | 5 |
Why: The relationship between the columns, not the individual numbers, is what generates the next row. Use obstacles = 0 before the loops.
Worked example
Your turn: count how many # pixels are in the image. Before revealing, predict the total by looking at the grid.
Hint
Why: Use obstacles = 0 before the loops. Add 1 inside the inner loop only when the pixel equals #.
image = [
['.', '.', '#', '.', '.'],
['.', '#', '#', '#', '.'],
['.', '.', '#', '.', '.'],
]
obstacles = 0
for row in image:
for pixel in row:
if pixel == '#':
obstacles = obstacles + 1
print(obstacles)Expected output
Why: One obstacle in row 0, three in row 1, and one in row 2: total 5.
| row | obstacles in row | running total |
|---|---|---|
| 0 | 1 | 1 |
| 1 | 3 | 4 |
| 2 | 1 | 5 |
| printed | - | 5 |
Reverse engineer
Discussion prompt
Work backwards. The example finished here:
Expected output
What was it asked to do, and what must it have been given? Reconstruct the problem from its answer.
Hint: Every quantity in the result had to enter somewhere. Account for each one.
Answer:
Your turn: count how many # pixels are in the image. Before revealing, predict the total by looking at the grid.
Missing information
Discussion prompt
Your turn: print the row and column of every obstacle. Before revealing, point to the five # positions in the grid.
What do you need to know — or decide — before the first line can be written? List everything the problem has to hand you.
Hint: Anything you would have to invent to get started is a thing the problem must supply.
Answer:
Coordinates need indexes, so use range(3) for rows and range(5) for columns.
Worked example
Your turn: print the row and column of every obstacle. Before revealing, point to the five # positions in the grid.
Hint
Why: Coordinates need indexes, so use range(3) for rows and range(5) for columns.
image = [
['.', '.', '#', '.', '.'],
['.', '#', '#', '#', '.'],
['.', '.', '#', '.', '.'],
]
for row in range(3):
for col in range(5):
if image[row][col] == '#':
print(row, col)Expected output
Why: The output lists each obstacle coordinate as row col.
| line | output |
|---|---|
| 1 | 0 2 |
| 2 | 1 1 |
| 3 | 1 2 |
| 4 | 1 3 |
| 5 | 2 2 |
Scale up
Step through it
Step through Your turn 3: print obstacle coordinates and watch the numbers move. Now imagine the input ten times bigger: which column is the one that stops this being practical?
Estimation
Predict first
Your turn: count every scanned position, not just obstacles. Before revealing, predict the final value.
Commit before you compute: what does Your turn 4: prove 15 positions come out to? A rough magnitude and the right form is enough — the point is to have something concrete to be wrong about.
Correct: Expected output
Why: A prediction you can defend turns the computation into a check rather than a leap of faith — and an answer that contradicts it is caught on the spot. Three rows times five columns means 15 total positions.
Worked example
Your turn: count every scanned position, not just obstacles. Before revealing, predict the final value.
Hint
Why: Create positions = 0 before the loops, then add 1 inside the inner loop for every pixel.
image = [
['.', '.', '#', '.', '.'],
['.', '#', '#', '#', '.'],
['.', '.', '#', '.', '.'],
]
positions = 0
for row in range(3):
for col in range(5):
positions = positions + 1
print(positions)Expected output
Why: Three rows times five columns means 15 total positions.
| row | columns scanned | running positions |
|---|---|---|
| 0 | 5 | 5 |
| 1 | 5 | 10 |
| 2 | 5 | 15 |
| printed | - | 15 |
Error analysis
Annotate
Walk the callouts on Your turn 4: prove 15 positions. Each one is a place this is easy to get subtly wrong.
positions = 0 before the loops, then add 1 inside the inner loop for every pixel.Ranking
Put in order
Put the moves of Full program: camera scanner into the order they have to happen.
Why: These are the moves of the worked example in the order it makes them, and each one is set up by the one before it. The outer loop prints one row at a time, then starts an inner scan for that row.
Worked example
Here is the whole scanner after the milestones are working. Yours can use the same shape with your own image.
image = [
['.', '.', '#', '.', '.'],
['.', '#', '#', '#', '.'],
['.', '.', '#', '.', '.'],
]
obstacles = 0
positions = 0
for row in range(3):
print('row', row, image[row])
for col in range(5):
positions = positions + 1
pixel = image[row][col]
if pixel == '#':
obstacles = obstacles + 1
print('obstacle at', row, col)
print('obstacles:', obstacles)
print('positions:', positions)Read the outer loop
Why: The outer loop prints one row at a time, then starts an inner scan for that row.
Read the inner loop
Why: Every pass adds one scanned position, then checks whether that pixel is an obstacle.
Expected output
Why: If yours prints your own rows, obstacle coordinates, 5 obstacles, and 15 positions in the same shape, you built it.
| line | output |
|---|---|
| 1 | row 0 ['.', '.', '#', '.', '.'] |
| 2 | obstacle at 0 2 |
| 3 | row 1 ['.', '#', '#', '#', '.'] |
| 4 | obstacle at 1 1 |
| 5 | obstacle at 1 2 |
| 6 | obstacle at 1 3 |
| 7 | row 2 ['.', '.', '#', '.', '.'] |
| 8 | obstacle at 2 2 |
| 9 | obstacles: 5 |
| 10 | positions: 15 |
Cost model
Annotate
In Full program: camera scanner, before reading the notes: mark where the time actually goes. Which line dominates?
Concept
Explain your scanner line by line: where the rows come from, where the columns come from, where the counters start, and where they change.
| question | answer you should be able to say |
|---|---|
Why range(3)? | There are 3 rows: 0, 1, 2. |
Why range(5)? | There are 5 columns: 0, 1, 2, 3, 4. |
| Why not include 5? | The stop value is excluded. |
| Why nested loops? | Each row needs a full column scan. |
The robot-camera idea is the same as real image logic: scan positions, inspect pixels, count or react to what you find.
Counterexample
Discussion prompt
Explain your scanner line by line: where the rows come from, where the columns come from, where the counters start, and where they change.
That is stated as though it always holds. Do one of two things: produce a case where it fails, or say precisely what rules such a case out. "It just does" is not on the menu.
Hint: Hunt at the extremes first — zero, one, negative, empty, equal. If every extreme survives, the reason they survive is the proof.
Answer:
The robot-camera idea is the same as real image logic: scan positions, inspect pixels, count or react to what you find.
Pattern
1. Decide whether the repeats are known
Why: Use for when you already have the items, rows, columns, or exact repeat count.
2. Choose the sequence
Why: Loop through a list for values, range(n) for exactly N repeats, or range(start, stop, step) for a scan pattern.
3. Name one item clearly
Why: Good loop variables sound like one thing: distance, pixel, row, col, or step.
4. Put repeated work inside the indented body
Why: Anything at the same indentation as the for line runs after the loop, not during every pass.
5. Trace one row per pass
Why: If you can fill a trace table, you can predict the loop before running it.
6. Use nested loops for grids
Why: Outer loop chooses the row; inner loop scans the columns.
Real world
Discussion prompt
Outside this lesson: where does for Loops as Robot Scanning Loops actually turn up? Name one concrete situation — a job, a piece of software someone ships, a decision somebody has to make — and say which part of The for-loop recipe is doing the work in it.
Hint: Vague is the failure mode here. "Engineering" is not a situation; "deciding whether this build is fast enough to ship" is.
Answer:
Pre-COSMOS Day 8 (Cluster 10: Robot Inventors), 60-minute robotics upgrade. for loops are taught as known-repeat robot scanning loops: process each sensor reading, repeat exactly N times with range(), scan every other position with range(start, stop, step), count obstacles, and use nested loops to scan a 3x5 camera image row by row.
Check
Trace it before clicking. How many values print, and what is the last one?
for i in range(5):
print(i)| pass | i | printed |
|---|---|---|
| 1 | ? | ? |
| 2 | ? | ? |
| 3 | ? | ? |
| 4 | ? | ? |
| 5 | ? | ? |
Check your understanding
What does range(5) print?
Answer: A
Why: range(5) starts at 0 and stops before 5, so it produces five values: 0, 1, 2, 3, 4.
Invariant
Step through it
Step through Check: range(5) one row at a time. One of these columns never changes — find it, and say why it cannot.
Elimination
Eliminate the wrong options
Which values does range(1, 10, 2) produce?
3 of these 4 are wrong. Strike them one at a time, and say what rules each one out before you strike the next. The survivor is the answer.
Survives elimination: A
Why: Start at 1, add 2 each time, and stop before 10. That gives 1, 3, 5, 7, 9.
Check
This is the exact end-excluded rule with a step. Predict it first.
for col in range(1, 10, 2):
print(col)| pass | col |
|---|---|
| 1 | ? |
| 2 | ? |
| 3 | ? |
| 4 | ? |
| 5 | ? |
Check your understanding
Which values does range(1, 10, 2) produce?
Answer: A
Why: Start at 1, add 2 each time, and stop before 10. That gives 1, 3, 5, 7, 9.
range().Trade off
Comparison matrix
From Check: range(start, stop, step): every row here is a choice with a cost. Fill the col column, then say which row you would actually pick and what you give up for it.
| pass | col |
|---|---|
| 1 | ? |
| 2 | ? |
| 3 | ? |
| 4 | ? |
| 5 | ? |
Check
A reading below 5 is an obstacle. Trace the counter before choosing.
readings = [12, 8, 3, 15, 4]
obstacles = 0
for distance in readings:
if distance < 5:
obstacles = obstacles + 1
print(obstacles)| distance | below 5? | counter after |
|---|---|---|
| 12 | False | ? |
| 8 | False | ? |
| 3 | True | ? |
| 15 | False | ? |
| 4 | True | ? |
Check your understanding
What does this program print?
Answer: A
Why: Only 3 and 4 are below 5, so the counter increases twice and prints 2.
Comparison
Comparison matrix
From Check: count close readings: refill the counter after column from what you know. The rest of the table is as it appeared.
| distance | below 5? | counter after |
|---|---|---|
| 12 | False | ? |
| 8 | False | ? |
| 3 | True | ? |
| 15 | False | ? |
| 4 | True | ? |
Check
A 3-row, 5-column camera image is scanned with one visit per row-column pair.
| row count | columns per row | total positions |
|---|---|---|
| 3 | 5 | ? |
Check your understanding
How many total positions does a 3x5 grid scan visit?
Answer: A
Why: The outer loop runs 3 rows, and the inner loop runs 5 columns for each row. 3 times 5 is 15 positions.
Elimination
Eliminate the wrong options
Which robot behavior is the best fit for a for loop?
3 of these 4 are wrong. Strike them one at a time, and say what rules each one out before you strike the next. The survivor is the answer.
Survives elimination: A
Why: A for loop is best when the count or the items are known in advance. Exactly 5 readings is known before the loop starts.
Check
Choose the behavior that best matches a for loop.
Check your understanding
Which robot behavior is the best fit for a for loop?
Answer: A
Why: A for loop is best when the count or the items are known in advance. Exactly 5 readings is known before the loop starts.
while better.while better.while better.Connect it up
Draw it
One page, no notation unless you need it: draw how these connect — The for-loop recipe · Why this matters for robots · For vs while · The shape of a for loop · The stop is a wall, not a tile. Put an arrow wherever one of them is what makes another possible, and label the arrow with why.
Recap
You can use for item in list to process every sensor reading, and range(n) to repeat exactly N times.
You can explain the big trap: range(5) gives 0, 1, 2, 3, 4 because the stop value is excluded.
You can use range(start, stop, step) for scan patterns, like every other column.
You can use counters, indexes, enumerate(), and nested loops to scan a 3x5 camera grid and count what the robot sees.
| robot problem | loop choice |
|---|---|
| known readings, rows, columns, or repeat count | for |
| unknown repeats until a sensor or goal changes | while |
Want this taught 1-on-1? Alexander tutors Python — $55/session, free consultation.