Computing route · lesson 1 of 5
- 01Procedures and stateUnseen
- 02Data and invariantsUnseen
- 03Algorithms and costUnseen
- 04Ownership and systemsUnseen
- 05Learning systems and evaluationUnseen
Computing / Field trial lesson 01
Procedures and state
Read a small program as a sequence of inspectable state changes, predict a loop before running it, and separate rebinding from mutation.
- Study time
- Awaiting learner observation
- Evidence
- Unseen
- Candidate
- 0.1-foundations-candidate
- Review
- Named human reviews pending
Debugging begins by slowing time down
A program does not execute as a paragraph. It advances through instructions, and each instruction sees a particular state. When the result surprises you, the useful question is not “What does this code look like it means?” but “What values were available immediately before this instruction, and what changed immediately after it?”
For this lesson, state means the bindings and object contents relevant to the next step. A trace is a compact record of those states. It turns execution from an invisible event into a sequence you can inspect, challenge, and reproduce.
width = 4
width = width + 3
area = width * 2
| statement | width | area |
|---|---|---|
| width = 4 | 4 | not bound |
| width = width + 3 | 7 | not bound |
| area = width * 2 | 7 | 14 |
Read the figure description
Row one executes width equals 4 and records width as 4. Row two evaluates the old width plus 3, then rebinds width to 7. Row three evaluates width times 2 and binds area to 14; width remains 7. The rows make evaluation order explicit.
A loop is repeated small work, not one mysterious leap
Python's for statement visits items from an iterable in order. On each visit, the loop target is bound to the next item and the body runs. You can therefore explain a loop by repeating the same local questions: What item is current? What is the accumulator before the body? What is it afterward?
An invariant is a statement that remains true at a chosen point in every iteration. For an accumulating sum, a useful invariant is: after the body finishes, total equals the sum of every item visited so far. The invariant is stronger than a guessed final answer because it explains every intermediate row.
total = 0
for item in [2, 4, 6]:
total = total + item
Worked example
Worked example — trace an accumulator
Explain the final value of total without running the code.
- Before the loop, total is 0 and no item has been visited. The invariant holds because the sum of no visited items is 0.
- First visit: item is 2. Evaluate old total plus item, which is 0 plus 2, then rebind total to 2.
- Second visit: item is 4. Evaluate 2 plus 4, then rebind total to 6.
- Third visit: item is 6. Evaluate 6 plus 6, then rebind total to 12.
- The iterable is exhausted. The invariant says total is the sum of 2, 4, and 6, so the final value is 12.
ConclusionThe trace 0, 2, 6, 12 explains both the final answer and the work performed at each step.
Function calls create a boundary, not a magical box
A function definition gives a procedure a name and parameters. Calling the function binds its parameter names to the supplied argument objects, executes the body, and returns a result. The local names belong to the call, but a mutable argument object may still be shared with the caller.
This distinction matters. Rebinding a local parameter changes which object that local name refers to. Mutating a shared list changes the contents of the object itself, so the caller can observe the change. A state trace should record both bindings and the contents of mutable objects when either can change.
def add_tax(prices):
taxed = []
for price in prices:
taxed.append(price * 1.05)
return taxed
original = [10, 20]
result = add_tax(original)
Worked example
Worked example — prove that the input list stays unchanged
Trace the call add_tax(original) and explain why original remains [10, 20].
- At the call boundary, the local name prices refers to the same list object as original.
- The statement taxed = [] creates a new empty list and binds only the local name taxed to it.
- Each loop iteration appends a computed value to the new taxed list. No statement targets prices or the original list object.
- The function returns the new list [10.5, 21.0], which is bound to result by the caller.
- The object referred to by original was read but never mutated, so its contents remain [10, 20].
ConclusionShared access does not imply mutation. The operations performed on each object determine what the caller observes.
Predict the next row before revealing it
Prediction before motion
With loop length 3 and input step 2, predict the current item and total after the second iteration. State the invariant in your own words before revealing the row.
- Invariant
- After each body execution, total equals the sum of every item visited so far.
- Observe
- Change the input step while holding loop length fixed. Which cells change, and why does the invariant remain true?
- Static account
- With items 2, 4, and 6, the before and after totals are 0 to 2, 2 to 6, and 6 to 12. Increasing the input step changes each item and total, but each after-total still equals the visited sum.
| Iteration | Current item | Total before | Total after | Invariant check |
|---|---|---|---|---|
| 1 | 2 | 0 | 2 | 2 equals sum of visited items |
| 2 | 4 | 2 | 6 | 6 equals sum of visited items |
| 3 | 6 | 6 | 12 | 12 equals sum of visited items |
Trace a conversion function without executing it
A trace is most useful when it predicts code you have not run. In the transfer task, a function converts temperatures into a new list. You must record the loop's state and preserve the distinction between the input object and the result object.
The assessment uses a structured state trace rather than another multiple-choice prompt. That change of form matters: demonstrated evidence should show that the same idea survives a new representation.
The frozen 0.1-preview release record retains the earlier sketch and manifest.