Loops

Loops let a program repeat a block of code — either a known number of times, or until some condition is no longer true — without duplicating that code manually for every repetition.

A loop repeats a block of code multiple times. A for loop is typically used when you know (or can compute) how many times to repeat, or want to iterate over each item in a collection. A while loop repeats as long as some condition remains true, useful when the number of repetitions isn't known in advance.

Without loops, repeating an action 100 times would mean writing that action's code 100 separate times. Loops let a program express 'do this repeatedly' concisely, and let the number of repetitions depend on data that isn't known until the program actually runs.

Processing every item in a shopping cart to compute a total, reading every line of a file, repeatedly polling a sensor until a stable reading is obtained, running a game's main loop every frame — loops appear constantly in real code.

A loop is like giving someone the instruction: 'wash each dish in the sink, one at a time, until the sink is empty.' You don't need to know in advance exactly how many dishes there are — the instruction naturally repeats until the stopping condition (an empty sink) is met.

range(5) produces the sequence 0, 1, 2, 3, 4. The for loop runs its body once for each value in that sequence, assigning the current value to i each time.

for loops are typically best for a known or countable number of iterations (a fixed range, or iterating over every element of a collection); while loops are typically best when the stopping condition depends on something computed during the loop itself, not known ahead of time.

break immediately exits the nearest enclosing loop entirely; continue skips the rest of the current iteration's body and proceeds directly to the next iteration's condition check.

An infinite loop (while (true) { ... } with no reachable break, or a for loop whose condition is never falsified) runs forever unless deliberately interrupted — sometimes intentional (a server's main event loop, an embedded system's main loop), sometimes an accidental bug.

Nested loops (a loop inside another loop) are common for processing multi-dimensional data (like a grid or matrix), but their total number of iterations multiplies — a 100-iteration outer loop containing a 100-iteration inner loop runs the innermost code 10,000 times, a fact that matters directly for performance.

Off-by-one errors — looping one too many or one too few times, often from confusing < with <=, or miscounting a range's start/end boundary. One of the single most common bugs in all of programming.

Writing an unintentional infinite loop by forgetting to update the loop's controlling variable, or updating it in a way that never actually reaches the exit condition.

Modifying a collection (adding or removing elements) while iterating over it directly, which in many languages produces undefined, inconsistent, or explicitly erroring behavior.

Using a deeply nested loop where a different algorithm or data structure could avoid the quadratic (or worse) time complexity entirely — a performance mistake covered further in the future Data Structures & Algorithms module.

Iterating directly over a collection's elements (for item in collection) rather than over numeric indices, when the index itself isn't actually needed — clearer and less error-prone.

Using a while loop with a sentinel condition to repeatedly read input (e.g. from a sensor or a file) until a specific terminating value or end-of-data signal is encountered.

Adding an explicit safety counter or timeout to a loop that waits for an external condition (like a hardware flag or network response), to avoid a genuine infinite hang if that condition never arrives.

Loops at the machine-code level A high-level loop compiles down to a comparison and a conditional branch instruction that jumps backward to the top of the loop's body as long as the condition holds — structurally, there's no separate 'loop instruction' on most CPUs; it's the same branching mechanism used for conditionals, just aimed backward in the instruction stream.

Loop unrolling and vectorization Compilers can transform loops significantly during optimization — loop unrolling duplicates a loop's body multiple times per iteration to reduce branching overhead, and auto-vectorization can convert a loop processing one element at a time into instructions that process several elements simultaneously using SIMD (Single Instruction, Multiple Data) CPU capabilities. Both are covered fully in the future Compiler Optimization and Computer Architecture modules.

Loop invariants and formal reasoning In formal program verification and algorithm analysis, a loop invariant is a condition that's true before the loop starts, remains true after every iteration, and — combined with the loop's exit condition — proves the loop achieves its intended result. This technique underlies rigorous algorithm-correctness proofs, covered further in the future Data Structures & Algorithms module.

What's the difference between break and continue?

break exits the entire loop immediately, skipping any remaining iterations. continue skips only the rest of the current iteration's body and moves straight to the next iteration's condition check.

What's an off-by-one error, and why is it so common?

It's a bug where a loop runs one iteration too many or too few — commonly caused by using < instead of <=, or vice versa, or miscounting a range's inclusive/exclusive boundaries. It's extremely common because boundary conditions are easy to reason about incorrectly even when the overall loop logic is otherwise sound.

When would you choose a while loop over a for loop?

When the number of iterations isn't known in advance and instead depends on a condition computed during the loop — for example, reading input until a specific sentinel value appears, or retrying an operation until it succeeds or a timeout is reached.

Debugging: a loop that should run 10 times only runs 9. What's a likely cause?

A likely off-by-one error — for example, using i < 10 with i starting at 1 instead of 0, or an incorrect boundary condition on the loop's range/condition that excludes one intended iteration.

for i in range(5):
    print(i)

# 0 1 2 3 4 — printed on separate lines