Control Flow in Practice
Part A is seven problems combining conditions and loops; from now on every function carries type annotations. Part B computes π two entirely different ways and makes you judge them against each other. Part C is viewing and editing files from the terminal.
Contents
What this session is for
- Combine conditions and loops to solve complete problems
- Judge when to use
forand when to usewhile - Start annotating every function with types
- Prepare for next week’s solo project A on recursion
Part A — required
4-1 Letter grades
Implement grade(score: int) -> str: "A" for 90 and above, "B" for 80–89, "C" for 70–79, "D" for 60–69, "F" below 60.
Return "Invalid" for anything outside 0–100.
grade(95) → "A"
grade(60) → "D"
grade(59) → "F"
grade(101) → "Invalid"
grade(-1) → "Invalid"
4-2 Sum of digits
Implement digit_sum(n: int) -> int, adding up the digits of a non-negative integer. Converting it to a string is not allowed.
digit_sum(0) → 0
digit_sum(12345) → 15
digit_sum(999) → 27
4-3 Primality
Implement is_prime(n: int) -> bool.
is_prime(1) → False
is_prime(2) → True
is_prime(97) → True
is_prime(91) → False # 91 = 7 × 13, and plenty of people miss it
4-4 Collatz step count
Implement collatz_steps(n: int) -> int: halve the even numbers, triple-plus-one the odd ones, and return how many steps it takes to reach 1.
collatz_steps(1) → 0
collatz_steps(6) → 8
collatz_steps(27) → 111
Then answer this in your submission notes: among the starting values 1 to 1000, which one takes the most steps, and how many?
4-5 Printing a shape
Implement triangle(n: int) -> str, returning an n-row triangle of asterisks as a single string with rows separated by \n and no trailing newline.
printing the return value of triangle(4) gives:
*
**
***
****
4-6 A guessing game
Write a program (not a function) that picks a random integer between 1 and 100, repeatedly reads a guess, says “too high” or “too low”, and reports how many guesses it took.
import random
answer = random.randint(1, 100)
This one is interactive: use print, not return. Include a transcript of one complete game in your submission notes.
4-7 Project warm-up: Towers of Hanoi
Implement hanoi_steps(n: int) -> int, returning the minimum number of moves needed to move n disks from one peg to another.
hanoi_steps(1) → 1
hanoi_steps(3) → 7
hanoi_steps(10) → 1023
A loop is enough for this. Next week, once you have recursion, you will solve it a completely different way — and comparing the two versions is where solo project A begins.
Write down the pattern you notice in your submission notes.
Part B — going deeper: computing π two ways, then judging them
This section asks you to compute the same constant with two entirely unrelated algorithms and then compare them. It is the first time this course asks you to evaluate an algorithm experimentally — not by being told which is better, but by running both.
Loops and arithmetic only; no lists needed.
B-1 The Leibniz series
Three centuries ago Leibniz found that
π/4 = 1 − 1/3 + 1/5 − 1/7 + 1/9 − ⋯
Implement leibniz(n: int) -> float, summing the first n terms.
Run it for n = 10, 100, 1000, 10000, printing each result next to math.pi along with
the error (abs(approx - math.pi)).
B-2 Find the pattern in the error
Put those errors in a table and stare at it. There is a very tidy pattern.
Answer:
- When
nis multiplied by 10, the error is divided by roughly what? - Following that pattern, roughly how many terms would you need for an error below
1e-6? - Run it and check. Estimate how long that will take before you start it.
B-3 Monte Carlo: computing π with random numbers
A completely different idea. Scatter random points over the unit square; the fraction
landing inside the quarter circle should approach π/4.
import random
x, y = random.random(), random.random() # each is a random number in [0, 1)
# (x, y) lies inside the quarter circle ⟺ x² + y² ≤ 1
Implement monte_carlo(n: int) -> float, estimating π from n points. Run it for
n = 1000, 10000, 100000 and record the results and errors.
B-4 The verdict
Put both sets of errors in one table and answer:
- For a million terms versus a million points, which is closer?
- How does the Monte Carlo error scale with
n? (Hint: it is not the pattern from B-2 — try multiplying the error by √n.) - To reach six correct decimal places, how many operations would each method need?
- So what is Monte Carlo good for? Think: what kind of problem admits no other approach?
Part C — fundamentals: viewing and editing files from the terminal
Last week you redirected output into a file. This week: how to look at that file, and how to edit it without leaving the terminal.
Looking: four commands
| Command | What it does |
|---|---|
cat file | print the whole thing. Only for small files |
head file | first 10 lines; head -n 3 for three |
tail file | last 10 lines; tail -f watches the file and shows new content as it arrives |
less file | page through it. space down, b up, /word to search, q to quit |
wc -l file | count the lines |
Editing: nano
nano filename opens the simplest of the terminal editors. The bottom two lines of the
screen are the help:
| Key | What it does |
|---|---|
Ctrl+O then Enter | save (O for write Out) |
Ctrl+X | exit |
Ctrl+W | search (Where is) |
C-1 Walk through it
Screenshots of the commands and their output go in your submission notes:
- Redirect your B-1 output into
pi.txt wc -l pi.txtto see how many lines it has- Look at it with
head -n 3 pi.txtandtail -n 3 pi.txt - Page through it with
less pi.txt, search for a digit with/, then quit withq nano pi.txt, add a title line at the top, save, exit, and confirm withhead -n 1 pi.txt
C-2 Think it through
- Why should you not use
caton a file with a million lines? - When is
tail -fthe right tool? (Hint: a program is still running and writing to a log.)
Before you submit
- Every function has type annotations
- 4-1 checks validity before anything else
- 4-2 handles
n = 0 - 4-3 has been tested with
91 - The questions in 4-4, 4-6 and 4-7 are answered in your submission notes