Course outline

Python / Foundations / Lesson 3

Decisions, loops, and invariants

Express rules as branches and repeated work as loops, then prove that each iteration makes progress and preserves a useful invariant.

About 4 min readContent reviewed
On this page

Lesson goals

  • Make conditions readable
  • Separate policy from traversal

Make conditions readable

An if statement selects a block when its condition is true. elif checks another condition only if earlier ones were false, and else covers the remaining cases. Order overlapping rules from the specific case to the broader fallback. Indentation defines the block; visual alignment is therefore part of the program’s meaning. Before writing code, list representative cases and boundaries in ordinary language. A shipping rule with free delivery at a threshold needs examples just below, at, and above that threshold. This technique reveals whether the requirement means greater than or greater than or equal to.

Iterate over values

A for loop requests successive values from an iterable. Prefer iterating directly over items when an index serves no purpose. range(start, stop, step) represents an arithmetic progression whose stop is excluded. That exclusion allows adjacent ranges to meet without overlapping and makes range(len(items)) cover valid sequence positions. Use enumerate when both a position and its item are needed. A loop can contain a branch without making the control flow mysterious: describe what each iteration consumes and what it contributes. Avoid changing the size of a collection while iterating over it unless the algorithm explicitly supports that behavior.

Accumulate with an invariant

An accumulator summarizes values seen so far. Initialize it before the loop, update it once per relevant item, and read it after the loop. An invariant is a statement true before and after every iteration; here total equals the sum of the nonnegative values already visited. Walking through the empty prefix establishes the starting value zero. Extending that prefix by one item explains the update. This reasoning is more reliable than checking only the final answer for one dataset. The example combines filtering and summation while making the rule and the changing state completely visible.

python Accumulate with an invariant — worked example
values = [4, -2, 7, 0]
total = 0
for value in values:
    if value >= 0:
        total += value
print(total)
Expected output
11

Negative two contributes nothing; four, seven, and zero preserve the stated invariant and produce eleven. An empty list would leave the initialized zero unchanged.

Termination and early exit

A while loop repeats while a condition remains true. Identify a quantity that moves toward termination and ensure every path updates it. break leaves the nearest loop, while continue starts its next iteration. These tools simplify a search when used sparingly. Python’s loop else runs when the loop ends without break, which can express a search failure; it is unrelated to an inner if. The example searches for the first even number, stops at its discovery, and produces a fallback only when the entire search is exhausted. Test both discovery and exhaustion when reviewing such a pattern.

python Termination and early exit — worked example
values = [3, 9, 12, 14]
for value in values:
    if value % 2 == 0:
        print("Found", value)
        break
else:
    print("No even value")
Expected output
Found 12

Twelve is the first matching value, so break prevents both the later fourteen and the loop’s else block from being processed.

Separate policy from traversal

A loop says how values are visited; the condition says which values matter. Keep those responsibilities distinguishable so a changed business rule does not force a rewritten traversal. Nested loops multiply work: for each outer item, the inner loop may inspect every inner item. Count approximate operations on a small example before applying that design to large data. Prefer an explicit readable loop over an expression with hidden side effects. During debugging, record iteration number, current item, and accumulator, then remove unnecessary tracing after the invariant and boundary cases have been verified. A terminating program can still implement the wrong rule.

Common errors and fixes

The threshold value follows the wrong branch

Why it happens
The comparison uses > where the requirement includes equality.
How to fix it
Translate the boundary rule into >= and verify adjacent cases.
Prevent it next time
Write below, equal, and above examples before coding.

A while loop never finishes

Why it happens
One execution path leaves its progress variable unchanged.
How to fix it
Update progress on every relevant path or use a bounded for loop.
Prevent it next time
State a decreasing remaining-work measure during review.

The total contains only the final item

Why it happens
The accumulator was reset inside the loop.
How to fix it
Move initialization before the loop and retain only the update inside.
Prevent it next time
Trace the accumulator after two consecutive iterations.

Check your understanding

1. Sum the odd numbers in range(1, 8). Explain the excluded endpoint.

Show worked solution

Initialize total to zero, iterate through range(1, 8), and add n when n % 2 != 0. The visited values are one through seven, so the sum is 1 + 3 + 5 + 7 = 16. Eight is not visited because stop is excluded.

2. Describe a search that returns a fallback for an empty sequence.

Show worked solution

A for loop with a matching branch and break can use its else for the fallback. With no items the loop immediately exhausts without break, so else executes. With a match it skips else. With only nonmatches it also reaches else after inspecting every item.

Key takeaways

  • Use boundary cases to validate branches.
  • An invariant explains what a loop has achieved so far.
  • Termination requires visible progress on every path.

Official references