Procedures Functions as Arguments - SICP Comparison Edition"/>
Consider the following three procedures. functions. The first computes the sum of the integers from a through b:
| Original | Python |
| (define (sum-integers a b) (if (> a b) 0 (+ a (sum-integers (+ a 1) b)))) | def sum_integers(a, b): return 0 if a > b else a + sum_integers(a + 1, b) |
| Original | Python |
| (define (sum-cubes a b) (if (> a b) 0 (+ (cube a) (sum-cubes (+ a 1) b)))) | def sum_cubes(a, b): return 0 if a > b else cube(a) + sum_cubes(a + 1, b) |
| Original | Python |
| (define (pi-sum a b) (if (> a b) 0 (+ (/ 1.0 (* a (+ a 2))) (pi-sum (+ a 4) b)))) | def pi_sum(a, b): return 0 if a > b else 1 / (a * (a + 2)) + pi_sum(a + 4, b) |
These three procedures functions clearly share a common underlying pattern. They are for the most part identical, differing only in the name of the procedure, function, the function of a used to compute the term to be added, and the function that provides the next value of a. We could generate each of the procedures functions by filling in slots in the same template:
| Original | Python |
| (define ($\langle name \rangle$ a b) (if (> a b) 0 (+ ($\langle term \rangle$ a) ($\langle name \rangle$ ($\langle next \rangle$ a) b)))) | def $name$(a, b): return (0 if a > b else $term$(a) + $name$($next$(a), b)) |
The presence of such a common pattern is strong evidence that there is a
useful
abstraction waiting to be brought to the surface. Indeed,
mathematicians long ago identified the abstraction of
summation of a series and invented sigma
notation,
for example
\[\begin{array}{lll}
\displaystyle\sum_{n=a}^{b}\ f(n)&=&f(a)+\cdots+f(b)
\end{array}\]
to express this concept. The power of sigma notation is that it allows
mathematicians to deal with the concept of summation itself rather than only
with particular sums—for example, to formulate general results about
sums that are independent of the particular series being summed.
Similarly, as program designers, we would like our language to be powerful
enough so that we can write a
procedure
function
that expresses the concept of summation itself rather than only
procedures
functions
that compute particular sums. We can do so readily in our
procedural
functional
language by taking the common template shown above and transforming the
slots
into
formal parameters:
parameters:
| Original | Python |
| (define (sum term a next b) (if (> a b) 0 (+ (term a) (sum term (next a) next b)))) | def sum(term, a, next, b): return 0 if a > b else term(a) + sum(term, next(a), next, b) |
| Original | Python |
| (define (inc n) (+ n 1)) (define (sum-cubes a b) (sum cube a inc b)) | def inc(n): return n + 1 def sum_cubes(a, b): return sum(cube, a, inc, b) |
| Original | Python |
| (sum-cubes 1 10) 3025 | print(sum_cubes(1, 10)) 3025 |
| Original | Python |
| (define (identity x) x) | def identity(x): return x |
| Original | Python |
| (define (sum-integers a b) (sum identity a inc b)) | def sum_integers(a, b): return sum(identity, a, inc, b) |
| Original | Python |
| (sum-integers 1 10) 55 | print(sum_integers(1, 10)) 55 |
| Original | Python |
| (define (pi-sum a b) (define (pi-term x) (/ 1.0 (* x (+ x 2)))) (define (pi-next x) (+ x 4)) (sum pi-term a pi-next b)) | def pi_sum(a, b): def pi_term(x): return 1 / (x * (x + 2)) def pi_next(x): return x + 4 return sum(pi_term, a, pi_next, b) |
| Original | Python |
| (* 8 (pi-sum 1 1000)) 3.139592655589783 | print(8 * pi_sum(1, 1000)) 3.139592655589783 |
Once we have sum, we can use it as a building block in formulating further concepts. For instance, the definite integral of a function $f$ between the limits $a$ and $b$ can be approximated numerically using the formula \[ \begin{array}{lll} \displaystyle\int_{a}^{b}f & = & \left[\,f\!\left( a+\dfrac{dx}{2} \right)\,+\,f\!\left(a+dx+\dfrac{dx}{2} \right)\,+\,f\!\left( a+2dx+\dfrac{dx}{2}\right)\,+\,\cdots \right] dx \end{array} \] for small values of $dx$. We can express this directly as a procedure: function:
| Original | Python |
| (define (integral f a b dx) (define (add-dx x) (+ x dx)) (* (sum f (+ a (/ dx 2)) add-dx b) dx)) | def integral(f, a, b, dx): def add_dx(x): return x + dx return sum(f, a + dx / 2, add_dx, b) * dx |
| Original | Python |
| (integral cube 0 1 0.01) 0.24998750000000042 | print(integral(cube, 0, 1, 0.01)) 0.24998750000000042 |
| Original | Python |
| (integral cube 0 1 0.001) | print(integral(cube, 0, 1, 0.001)) 0.249999875000001 |
| Original | Python |
| def inc(k): return k + 1 def simpsons_rule_integral(f, a, b, n): def helper(h): def y(k): return f((k * h) + a) def term(k): return (y(k) if k == 0 or k == n else 2 * y(k) if k % 2 == 0 else 4 * y(k)) return sum(term, 0, inc, n) * (h / 3) return helper((b - a) / n) |
| Original | Python |
| (define (sum term a next b) (define (iter a result) (if ?? ?? (iter ?? ??))) (iter ?? ??)) | def sum(term, a, next, b): def iterate(a, result): return ($\langle{}$??$\rangle$ if $\langle{}$??$\rangle$ else iterate($\langle{}$??$\rangle$, $\langle{}$??$\rangle$)) return iterate($\langle{}$??$\rangle$, $\langle{}$??$\rangle$) |
| Original | Python |
| # recursive process def product_r(term, a, next, b): return 1 if a > b else term(a) * product_r(term, next(a), next, b) # iterative process def product_i(term, a, next, b): def iterate(a, result): return result if a > b else iterate(next(a), term(a) * result) return iterate(a, 1) |
| Original | Python |
| (accumulate combiner null-value term a next b) | accumulate(combiner, null_value, term, a, next, b) |
| Original | Python |
| # recursive process def accumulate_r(combiner, null_value, term, a, next, b): return (null_value if a > b else combiner(term(a), accumulate_r(combiner, null_value, term, next(a), next, b))) def sum_r(term, a, next, b): def plus(x, y): return x + y return accumulate_r(plus, 0, term, a, next, b) def product_r(term, a, next, b): def times(x, y): return x * y return accumulate_r(times, 1, term, a, next, b) # iterative process def accumulate_i(combiner, null_value, term, a, next, b): def iterate(a, result): return result if a > b else iterate(next(a), combiner(term(a), result)) return iterate(a, null_value) def sum_i(term, a, next, b): def plus(x, y): return x + y return accumulate_i(plus, 0, term, a, next, b) def product_i(term, a, next, b): def times(x, y): return x * y return accumulate_i(times, 1, term, a, next, b) |
| Original | Python |
| def filtered_accumulate(combiner, null_value, term, a, next, b, filter): return (null_value if a > b else combiner(term(a), filtered_accumulate(combiner, null_value, term, next(a), next, b, filter)) if filter(a) else filtered_accumulate(combiner, null_value, term, next(a), next, b, filter)) |