Week 6 — Lecture Outline · Loops II: `for`, `range` & Nested Loops
Course: Introduction to Computer Science — CS1 / Programming Fundamentals in Python (CSCI 1101) · Silver Oak University (fictional sample) · Prof. Okafor
Objective covered: Objective 4 — Write and trace loops, including the for loop, range() (start / stop / step, with an exclusive stop), iterating over a string, accumulating a total, and nested loops.
SLOs touched: A (write and run a correct loop) · B (trace a loop / nested loop and predict its exact output and line count; find and fix an off-by-one)
Meeting pattern: 2 studio sessions × 75 min = 150 min. Segment minutes below total ~150; scale to your own pattern.
Every code output, traced value, range listing, loop total, and nested-loop output in this outline was produced by actually running the code in Python — not hand-traced. Run them live in class; the outputs are exact.
Week at a Glance
| The week's big question | "When you know how many times to repeat — how do you say it, and what exactly does range() give you?" |
| By the end of the week, students can… | (1) write a for loop over range() and over a string; (2) predict range(stop) / range(start, stop) / range(start, stop, step) exactly and explain why the stop is exclusive; (3) accumulate a running total with for; (4) trace a nested loop, predict its exact output, and count how many lines it prints. |
| Key vocabulary | for loop, iterate / iteration, iterable, sequence, range(stop), range(start, stop), range(start, stop, step), start, stop (exclusive / "up to but not including"), step, loop variable, accumulator, running total, nested loop, inner loop, outer loop, off-by-one error, range(1, n + 1) |
| Materials | slides (Deck 6), the week's readings + video links, a free online Python environment + Python Tutor (links in the readings), one approved chatbot (Gemini / Claude / ChatGPT) for the AI-critique moment and the tutorial |
| Timing note | 8 segments, ~150 min total. Session 1 = Segments 1–4 (~75). Session 2 = Segments 5–8 (~75). This is a studio: code along on the projector and have students type every example into the online environment with you — and print(list(range(...))) whenever a range's contents are in doubt. |
Segment 1 — Hook & the Promise (8 min) · Session 1 opens
Hook. Put a tiny challenge on a slide: "Print every number from 1 to 5, each on its own line. With last week's while loop, that's four lines: set a counter, write the condition, print, increment." Write it out. Then say: "This week, the same job is one line: for i in range(1, 6):." Pause. "But notice I wrote 6, not 5. If I'd written range(1, 5) — which looks exactly right — it would stop at 4. That single off-by-one is the most common bug in all of programming, and by Friday you'll never make it again."
The promise (write it on the board): "By Friday you'll write for loops, predict exactly what any range() produces, sum a series, and trace a loop-inside-a-loop line by line — and you'll know cold that range stops before the stop."
Why it matters line (memory hook): "range stops before the stop — the last number is one less than you wrote."
Segment 2 — The for Loop & Iteration (18 min)
Plain language first. A for loop says: "do this block once for each item in a sequence." Where while repeats as long as a condition is true, for repeats across a known collection of items — and it handles the counter for you. No manual increment, no condition to get wrong.
Code-along #1 — for over a string (everyone types it):
for ch in "cat":
print(ch)
Run it. Output (run-verified):
c
a
t
"The loop variable ch takes each character in turn — c, then a, then t — and the indented block runs once for each. The string is the sequence; the loop walks it left to right. Indentation marks the body, exactly like last week."
Name the anatomy on a slide: for in : then an indented body. The loop variable is yours to name (ch, letter, x), and Python reassigns it on each pass — you don't touch it.
The clarification students always need: the for loop never asks a condition. It doesn't run "while something is true"; it runs once per item, then stops on its own when the sequence is exhausted. That's why a for loop can't run forever the way a while loop can.
Segment 3 — range(): the Counting Tool (and the Exclusive Stop) (24 min) · the code-along
Set it up: "To count, we need a sequence of numbers. range() makes one. But range() has a rule that trips up everyone, so we're going to list its contents until it's obvious."
The list-it trick — use it all segment: range(...) doesn't print as a list of numbers on its own, so to see what it holds, wrap it: print(list(range(...))).
Code-along, step by step (everyone types each line and runs it):
Form 1 — range(stop) starts at 0 and stops before stop:
print(list(range(5)))
Output (run-verified):
[0, 1, 2, 3, 4]
"Five numbers — but they're 0 through 4, not 1 through 5. range(5) means 'five values starting at 0,' so it ends at 4. The 5 is the stop, and the stop is excluded — Python goes up to but not including it."
Form 2 — range(start, stop) lets you choose where to begin:
print(list(range(1, 5)))
Output (run-verified):
[1, 2, 3, 4]
"Starts at 1, stops before 5. So 1, 2, 3, 4. This is THE one to remember: it does not include 5. Want the 5? Write range(1, 6)." Show it:
print(list(range(1, 6)))
Output (run-verified):
[1, 2, 3, 4, 5]
Form 3 — range(start, stop, step) adds a stride:
print(list(range(0, 10, 2)))
Output (run-verified):
[0, 2, 4, 6, 8]
"Step 2 means skip-count by twos: 0, 2, 4, 6, 8. Still stops before 10, so 10 is not in it. A negative step counts down:"
print(list(range(5, 0, -1)))
Output (run-verified):
[5, 4, 3, 2, 1]
"Start 5, step -1, stop before 0 — so 5, 4, 3, 2, 1, and 0 is excluded, same rule going down."
Now drive a loop with it (the code-along payoff):
for i in range(1, 5):
print(i)
Output (run-verified):
1
2
3
4
"Four lines — 1 to 4. The 5 never prints, because the stop is exclusive. Say it with me: range stops before the stop."
Quick interaction (rapid-fire, ~4 min): put four range(...) calls on a slide; for each, students predict the contents solo (20 sec), then you run print(list(...)). Suggested with run-verified outputs: range(3) → [0, 1, 2]; range(2, 8) → [2, 3, 4, 5, 6, 7]; range(1, 10, 3) → [1, 4, 7]; range(10, 0, -2) → [10, 8, 6, 4, 2].
Segment 4 — Accumulating with a for Loop (16 min) · Session 1 closes (~75)
Set it up: "A huge use of a counting loop is to build up an answer — a running total, a longest streak, a string. The pattern: start with an empty/zero accumulator before the loop, and update it inside the loop."
Worked trace — sum 1 through 5 (reveal it, predict, then run):
total = 0
for n in range(1, 6):
total = total + n
print(total)
- Trace:
totalstarts at0. Pass 1:n = 1,total = 1. Pass 2:n = 2,total = 3. Pass 3:n = 3,total = 6. Pass 4:n = 4,total = 10. Pass 5:n = 5,total = 15. Then the loop ends and we print. - Predicted output:
15. Run it. Actual output (run-verified):
15
"Two things to notice. One: range(1, 6) — the 6 is there so 5 is included in the sum. Two: print(total) is outside the loop (not indented), so it runs once, after all the adding. Indent it by mistake and it prints five times."
Show the shorthand: total = total + n can be written total += n — same thing. Run it again, still 15 (run-verified).
The off-by-one preview (sets up Segment 6): "If I'd written range(1, 5) here, I'd be summing 1 + 2 + 3 + 4 = 10, silently missing the 5. The program wouldn't crash — it would just be wrong. That's the dangerous kind of bug, and it's this week's villain." (Run-verified: that version prints 10.)
Segment 5 — Nested Loops: a Loop Inside a Loop (22 min) · Session 2 opens
Hook back in: "Last session: one loop. Today: a loop inside a loop. This is how you build a grid, a table, or anything with rows and columns — and the key idea is that the inner loop runs all the way through for every single step of the outer loop."
Plain language first. When one for loop sits inside another, the outer loop takes one step, and for that one step the inner loop runs completely — start to finish — before the outer loop takes its next step. If the outer runs 3 times and the inner runs 4 times, the inner body runs 3 × 4 = 12 times total.
Code-along — a 3×3 grid of stars (build it live):
for r in range(3):
for c in range(3):
print("*", end="")
print()
Run it. Output (run-verified):
***
***
***
"Read it slowly. The outer loop for r in range(3) runs 3 times — once per row. Each time, the inner loop for c in range(3) runs fully, printing three * on the same line — that's what end="" does, it keeps printing on one line instead of dropping down. Then print() (indented under the outer loop, not the inner) ends the row with a newline. Three rows, three stars each."
Make the structure visible — count the lines. "How many lines did that print? Three — one per outer step. The inner loop doesn't make new lines; the outer loop's print() does. That line count is your fastest check on a nested loop."
Code-along — a small multiplication table (the classic):
for i in range(1, 4):
for j in range(1, 4):
print(i * j, end=" ")
print()
Run it. Output (run-verified):
1 2 3
2 4 6
3 6 9
"Outer i is the row (1, 2, 3); inner j is the column (1, 2, 3). Each cell prints i * j. Row i = 2 gives 2, 4, 6. Nine cells, three rows. The end=" " puts a space between cells; the print() after the inner loop ends each row."
Segment 6 — Trace a Nested Loop + Spot & Fix the Off-by-One (20 min)
Worked trace — predict the exact output AND the line count (reveal, predict, run):
for i in range(2):
for j in range(3):
print(i, j)
- Trace: outer
iis0, then1. Fori = 0, innerjruns0, 1, 2→ prints0 0,0 1,0 2. Fori = 1, innerjruns0, 1, 2again → prints1 0,1 1,1 2. Six lines (hereprintis inside the inner loop, so every inner pass makes a line:2 × 3 = 6). - Predicted output / line count: 6 lines. Run it. Actual output (run-verified):
0 0
0 1
0 2
1 0
1 1
1 2
"Contrast with the grid: there print() was under the outer loop, so 3 lines; here print(i, j) is under the inner loop, so 6 lines. Where the print sits decides the line count."
Spot & fix the bug (the debugging demo) — the exclusive-stop off-by-one.
The buggy program (put it on a slide exactly as is). "A student wants to print 1 through 5 and writes:"
for i in range(1, 5):
print(i)
Debug it out loud:
1. Run it. Output (run-verified):
1
2
3
4
- "It printed
1to4. The5is missing. No error, no red text — the program runs fine and is just wrong. That's the off-by-one." - The bug:
range(1, 5)stops before5. The stop is exclusive. - The fix, then run to confirm:
for i in range(1, 6):
print(i)
Output (run-verified):
1
2
3
4
5
"To include an endpoint n, the stop has to be n + 1. Want 1 to n? Write range(1, n + 1). That + 1 is doing real, load-bearing work."
Land the key idea: the most dangerous bug doesn't crash. An off-by-one produces an answer that looks plausible but is one short (or one long). The cure is the same as always: run it, count the output, and check the endpoints.
A second quick bug (let the room solve it). "This is meant to print a 3-line grid but prints 9 lines:"
for r in range(3):
for c in range(3):
print("*")
Run-verified: it prints nine *, each on its own line — because print("*") is inside the inner loop and each call makes a newline (and there's no end=""). The fix to get a 3×3 block: print("*", end="") in the inner loop and a print() under the outer loop (the Segment 5 version).
Segment 7 — Misconceptions Round-Up + Quick Interaction (20 min)
Name the misconceptions out loud, then cure each:
- ❌ "
range(1, 5)gives1, 2, 3, 4, 5."
✅ Cure: the stop is exclusive —range(1, 5)is1, 2, 3, 4. List it:print(list(range(1, 5))). For the5, userange(1, 6). - ❌ "
range(5)starts at 1."
✅ Cure:range(stop)starts at 0 →0, 1, 2, 3, 4. Five values, but0-based. - ❌ "The inner loop runs once and then the outer continues."
✅ Cure: the inner loop runs completely for every step of the outer loop. Outer 3 × inner 4 = 12 inner passes. - ❌ "
range(0, 10, 2)includes 10."
✅ Cure: stop is still exclusive with a step —0, 2, 4, 6, 8.10is not in it. - ❌ "To sum 1 to n, loop over
range(1, n)."
✅ Cure: that missesn. Userange(1, n + 1). The endpoint needs+ 1.
Interaction — Predict-then-Run (rapid-fire, ~8 min): put four items on a slide; students predict each output solo (30 sec), compare with a neighbor (1 min), then you run all four. Suggested with run-verified outputs:
- print(list(range(1, 5))) → [1, 2, 3, 4]
- print(list(range(0, 10, 2))) → [0, 2, 4, 6, 8]
- a sum loop total = 0 / for n in range(1, 11): total += n / print(total) → 55
- the nested coords loop above → 6 lines ending 1 2
Tally how many the room got right — celebrate the off-by-one misses as "this is exactly why we list the range and run the loop."
Segment 8 — Technology Workflow + AI-Critique, Callback & Hand-off (18 min) · Session 2 closes (~75)
Technology workflow — the everyday loop, on demand:
1. Open the free online Python environment (link in the readings). Write your loop.
2. Unsure what a range(...) holds? Wrap it: print(list(range(...))) and read the exact contents.
3. Run the loop. Count the output lines — for a nested loop, the line count is your fastest correctness check.
4. To watch a nested loop run step by step, paste it into Python Tutor and click forward; observe the inner loop fully cycle before the outer advances.
AI-critique moment (students verify, not consume):
Paste this to an approved chatbot: "Write a Python
forloop that prints the numbers 1 through 5, and tell me exactly what it prints. Then tell me how many lines this prints:for i in range(3):/for j in range(3):/print(i, j)."
Then check its work by running both yourself. Two classic AI slips this week: (1) it may write the "1 through 5" loop asrange(1, 5)— which actually stops at 4 — or claimrange(1, 5)includes 5; (2) it may miscount the nested loop (the correct answer is 9 lines, because the3 × 3). Your job all semester: the tool drafts, you run it and judge. This is exactly how the weekly Lecture Tutorial and Coding Lab work — you catch the model, not trust it.
Callback + tease:
- Callback: "Last week, while — repeat as long as a condition holds. This week, for with range() — repeat a known number of times — plus nested loops for grids and tables. Two loops, two tools; you'll reach for for most."
- Tease next week: "Our programs are getting long, and we keep repeating chunks of logic. Next week we package code into reusable, named pieces — functions. We'll meet def, parameters and arguments, return, and the difference between local and global scope. A function is a loop's natural teammate."
Hand-off (the week's graded work):
- Lecture Tutorial 6 (AI tutor, share-link submission) — for, range (exclusive stop, step), iterating a string, accumulating, and nested loops.
- Quiz 6 and Discussion 6 ("for vs while: Is One Always Better?") and Assignment 6 ("Loops that Count & Build").
- Coding Lab 6 — "Counting, Ranges & Grids" — write an accumulator and a nested-loop pattern, predict-then-run a range() table, and fix an off-by-one in the online environment.
Instructor FAQ — Common Stumbles
| Student says / does | Quick cure |
|---|---|
"range(1, 5) should print 5 numbers." |
List it: print(list(range(1, 5))) → [1, 2, 3, 4]. The stop is excluded — four numbers. |
Wants 1 to n, writes range(1, n). |
That stops at n - 1. Use range(1, n + 1) to include n. |
Surprised range(5) is 0, 1, 2, 3, 4. |
range(stop) starts at 0. Five values, zero-based. |
Thinks range(0, 10, 2) includes 10. |
Stop stays exclusive even with a step: 0, 2, 4, 6, 8. Run list(range(0, 10, 2)). |
| Nested loop prints too many / too few lines. | Ask: is the print() under the inner loop (a line per inner pass) or the outer loop (a line per row)? That decides the count. |
| Thinks the inner loop runs once per program. | The inner loop runs fully for every outer step: outer × inner total passes. |
| Sum comes out one short. | Classic off-by-one — the stop excludes the last value. Check range(1, n + 1). |
print(total) runs many times. |
It's indented into the loop. Move it left so it's after the loop, not inside it. |
| All cells run together with no spacing. | The grid used end=""; add end=" " for spaces, and a print() after the inner loop to break the row. |
Scope flag
This outline stays within Objective 4 as taught in Week 6: the for loop, range() (stop / start, stop / start, stop, step, with an exclusive stop), iterating over a string, accumulating a total, and nested loops (and counting their output). It builds directly on the while loop, counters, and accumulators from Week 5 and the indentation/print work from Weeks 1–4. Lists (iterating for x in my_list), enumerate, list comprehensions, break/continue, and else-on-loops are later and are not used here (lists arrive in Week 9). We use only for, range, simple arithmetic, print(..., end=...), and accumulators — the right floor for this week. Python's range semantics are referenced factually; the instructor and institution remain fictional. Every output, range listing, and line count shown was produced by running the code.
~ Prof. Okafor's edition · Fall 2026 · built with thecoursemaker.com