2025-06-21

iteration, loops, and recursion

this section outlines the main choices when designing loops. recursion, iteration, and higher-order forms like map or fold are interchangeable; the key is understanding when and how to use each based on termination, state, and structure.

design variables

each loop construct can be analyzed along the following axes:

  • termination: how and when the loop stops

    • explicit condition (e.g. index bound, sentinel value)
    • implicit in recursion via base case
    • bounded iteration over structure (e.g. list, range)
  • state management: what data is updated across iterations

    • single accumulator (e.g. in fold)
    • multiple accumulators or local bindings
    • in-place mutation (imperative) vs structural updates (functional)
  • composition style: how the loop integrates with surrounding code

    • inline loop with side effects
    • returned data structure
    • higher-order combinators (map, filter, fold)
    • tail-recursive self-call
  • control flow abstraction: how much of the control is abstracted

    • full control: explicit loop or recursion

    • partial abstraction: fold, unfold, etc.

    • total abstraction: lazy sequences, generators

common iteration forms

  • map: applies a function to each element and returns a new structure

    • one input, one output
    • no internal state across iterations
  • fold/reduce: accumulates a result by applying a function across elements

    • single state variable updated per step
    • suitable for scalar summarization or structural transformation
  • unfold: generates a structure from a seed by repeated transformation

    • inverse of fold
    • useful for stream generation or constructing lists recursively
  • custom recursive loop: uses a named recursive binding (e.g. let loop)

    • full control over state and branching

    • allows multiple outputs and mixed control flows

example: custom recursive map

in scheme

(let loop ((rest (list 1 2 3)))
  (if (null? rest) rest
    (cons (+ 1 (car rest)) (loop (cdr rest)))))

this implements a basic map operation by:

  • checking termination via (null? rest)
  • recursively calling loop with (cdr rest)
  • producing a new list via cons and (+ 1 ...)

this style generalizes easily - for example, by accumulating additional values, branching conditionally, or logging state.

considerations when designing a loop

  • identify the stop condition early

    • in recursion: base case
    • in loops: compare indices, flags, or structure traversal
  • determine what state changes per iteration

    • single or multiple variables
    • structural updates or in-place mutation
  • decide how much abstraction is acceptable

    • simple transformation -> use map, fold
    • multi-output or conditional branching → use explicit recursion or let loop
  • consider composition

    • is the result fed to another transformation?
    • does the loop perform side effects or produce a return value?