A Python function is a value: bind it to another name, store it in a collection, pass it as an argument, or return it. You have already called functions; now use that value model to separate a reusable traversal from the policy it applies. A higher-order function accepts a function or returns one. No special lambda syntax is required: a named def works just as well.
def visible(word):
return len(word) >= 3
def shout(word):
return word.upper()
def select_map(words, keep, change):
return tuple(change(word) for word in words if keep(word))
policy = visible
print(select_map(["a", "elm", "oak"], policy, shout))Pass shout, not shout(). The former is the callable; the latter tries to call it immediately and passes its returned value. An ordinary function definition is often clearer than an increasingly long lambda because it gives the policy a name, a test seam and room for a useful error message. A lambda has ordinary closure rules and no special purity guarantee.
Write the callback signatures as part of the helper contract. In this lesson, predicate(item) sees the original item. Only accepted items reach transform(item). Testing a transformed value instead would define another useful helper, but it would be a different contract. The distinction changes types, invocation counts, work and error timing. A transform that divides by its input must never receive a rejected zero.
The build first collects results into a tuple so the selection result can be revisited. The source itself may be traversed once. A callback exception immediately leaves the current call; returning a default would hide failure. Already-performed callback effects cannot be undone, which is another reason to prefer input-preserving deterministic policies in ordinary transformations.
Consuming a supplied iterator changes its position. With reusable value inputs and pure callbacks, this helper behaves as a pure transformation; with a caller-owned iterator, consumption is an explicit effect. Function parameters make a policy visible without erasing its state or effects.
A left fold generalizes accumulation. Its step accepts an accumulated state and the next item, then returns the next state. The initial state makes emptiness meaningful and can differ in type from each item. A tuple accumulator can collect strings, while an integer accumulator can count characters.
from functools import reduce
print(reduce(lambda state, item: state - item, [3, 2], 10))
print(reduce(lambda state, item: state + (item,), ["a", "b"], ()))The subtraction example is deliberately noncommutative: changing argument order or grouping changes the result. After each step, state represents the completed input prefix. For no items, the explicit initial object is returned unchanged. A mutable initial object can still be mutated by an ill-behaved step; the fold is not an isolation mechanism. Use sum, any, all, a comprehension, or a plain loop when one states the intention more clearly than a generic fold.
In the checks, trace function values separately from invocations. The bug hunts expose transforming too early and swapping accumulator arguments. During the build, use callbacks that record their calls to inspect ordering, then remove those effects from production policies. The test callback is an observation tool; it is not a claim that higher-order code automatically makes effects disappear.