Decorator ➔ a HOF wrapping a function (@dec is f = dec(f)) ➔ memoisation rescues naive Fibonacci.
⚙️ Core Implementation
🔹 Taking, returning, and the three list HOFs
closures + map/filter/reduce
def my_call(f, x): f(x) # takes a functiondef return_f(x): # returns a CLOSURE capturing x def f(y): return x + y return fg = return_f(30); g(2) # 32 ('add 30')list(map(lambda x: x*2, [1,2,3])) # map: [2,4,6]list(filter(lambda x: x % 2, [1,2,3,4])) # filter: [1,3]from functools import reducereduce(lambda a,b: a+b, [1,2,3,4], 0) # reduce/fold: 10
💡 Common Mistake:A closure keeps a reference to the captured variable ➔ even after the enclosing call returns — g = return_f(30) behaves as “add 30” forever; this is how HOFs build configured functions at run time.
⚖️ Core Decision Matrix
Tool
Form
Replaces / adds
map
map(f, xs)
a transform loop
filter
filter(p, xs)
a select loop
reduce/fold
reduce(g, xs, init)
an accumulate loop
closure
inner fn capturing scope
configured functions
partial/curry
partial(f, a)
specialise a general fn
decorator
@dec = f = dec(f)
wrap (logging, memoise, timing)
When It Flips: abstraction/reuse (one traverse(tree, f) covers infinitely many behaviours) + composability (f∘g pipelines); the trade-off is that nested closures / point-free style can obscure control flow.
📊 Exam Execution Trace
Manual Execution Trace
A lazy select→transform→aggregate pipeline:
Step / State
Stage
Call
Streams
0 (Init)
—
—
—
1
filter
filter(p, xs)
keep odds
2
map
map(f, ...)
square each
3
reduce
reduce(add, ...)
sum
With generators the whole pipeline is O(1) memory (no intermediate lists).
Applied Exercise
Problem: Show a decorator is a HOF.
Derivation Proof / Hand-Calculation Walkthrough: