You are on page 1of 18

Templates

by
John Carlis

(You should have read SICP through Section 1.2.2 — up to page 37.)

This missive will help you, via templates, to become wizardly. Besides just reading you must
study the templates and work the exercises that use and abuse them.

Often you will compute using recursive templates (aka patterns). You should learn these
templates, look for them when you read code, and consciously employ them in a disciplined way
when you write code. If you are undisciplined, you will have avoidable programming
“adventures”, akin to a chemist refusing to use the periodic table – very un-wizardly. To be
disciplined means that you consistently use conventions and templates. This allows you to
quickly write code that is obviously correct. Why quick? Well, when you are knowledgeable
and skilled you have command of a topic, ready at your fingertips – a master chef does not
dawdle over which pan to use. Why obvious? Well, if you hack up something, and say “I hope
this works” then you are behaving un-wizardly. Obvious connotes that you design the code
without doubt, you write up the design (quickly), check it visually, conclude it is correct, and
then it indeed actually works. When your code fails, of course this happens often to all of us,
even wizards, you switch into problem solving mode, examine symptoms, formulate alternative
responses, decide on a response, and move on. A more crass reason for learning the templates is
that you will do better on 1901’s tests.

Many procedures loop (recurse) until some condition is satisfied. Here are four condition
templates for such computing:

• Never-ending. You do not plan for any end, looping infinitely. Since this really cannot
happen, in some way you or the Scheme interpreter must abort processing. [With some
interpreters “^c” (hitting “c” while holding down the control key) works. So does
unplugging the computer.]
• No more data. When processing a list you reach its end. For file processing, you get an
“EOF”, that is, an end of file return. For examples of this template you will have to wait
until Chapter 2.
• Close enough. Commonly close enough appears as something like |x| < ε or, in
words, “absolute value of x is less than epsilon”, where epsilon, the precision such as
“0.0001”, is supplied as an operand.
• Counting. When processing you count, satisfying a condition which tests that you’ve
counted the correct number of times (often “n”).

Close enough is addressed next in Part I, and Parts II, III and IV address counting. Part V
addresses defensive programming to prevent infinite loops and other problems.
PART I Close Enough

While SICP, page 23 has one close enough example, here are two versions of an even simpler
(and not very useful) example. The task is to display a series of random numbers between zero
and 1, continuing until coming across a number that is less than an operand called epsilon. A
system supplied procedure called random somehow determines a number – perhaps it rolls dice.
Notice that both versions use a block structure template [which has procedures defined inside a
procedure, and those nested procedures are invisible to the outside of the containing block
(procedure)] and version 2 uses a helper procedure (SICP, page 30) [which is called at the last
expression in the containing procedure].

; version 1 ;version 2
(define (series-of-random epsilon) (define (series-of-random epsilon)
(define (series-of-random-helper)
(define number (random 1.0) ) (define number (random 1.0) )
(display number) (display number)
(display "\n") (display "\n")
(if (< number epsilon) (if (< number epsilon)
"That's all folks" "That's all folks"
(series-of-random epsilon) (series-of-random-helper)
) )
)
(series-of-random-helper)
) )

Warning. Random is idiosyncratic, which means that it is implemented differently in different


Scheme interpreters. Some, as above, return a floating point (aka real) number between zero and
its floating point number parameter. Others return an integer number between zero and its
integer number parameter. You will need to check – and to document this possible point of
failure.

WORK. Study these versions, determining their similarities and differences. Change these
versions so that the last number, which is less than epsilon, is not displayed.
PART II Counting
Some procedures perform arithmetic and satisfy some counting condition. Here are four counting
templates that are defined by picking one element from each of two dimensions (with convenient
abbreviations DU, DD, RU, RD). One dimension is where the arithmetic work gets done.
• D Deferred computation
• R Running totals
The other dimension is how you control counting while recursing (also called iterating or
looping).
• U Counting up from 1 to n by 1
• D Counting down from n to 1 by 1

Sometimes you will care about only the up/down dimension.

Example.

These templates apply when you create procedures to compute recurrence relations. (CSCI 2011
covers recurrence relations in depth.) For example, you know from first grade or earlier that
multiplying natural numbers is really just adding:

a*b=a+a+a… for b occurrences of a.

Expressed in recurrence relation form this is:

multiply-by-adding (a,1) = a.
multiply-by-adding (a,b) = a + multiply-by-adding (a, b-1)

Below are four versions of multiply-by-adding which illustrate the templates. If a is 3 and b is 4,
evaluating each version yields the same answer, 12. Notice that b must be a positive integer, and
that none of these versions work if b is not a positive integer.

WORK. You should determine what happens (and why) in each version if b is: 0, negative, or
not an integer.
DD DU RD RU
deferred computation; deferred computation; running totals; running totals;
down from n to 1 by 1 up from 1 to n by 1 down from n to 1 by 1 up from 1 to n by 1

(define (multiply-by- (define (multiply- (define (multiply-by- (define (multiply-by-


adding-DD a b) by-adding-UD a b) adding-DR a b) adding-UR a b)

(define (multiply-by- (define (multiply- (define (multiply-by- (define (multiply-by-


adding-helper by-adding-helper adding-helper adding-helper
a a total total
now now now now
) ) ) )

(if (if (if (if


(= now 1) (= now b) (= now 1) (= now b)
a a (+ total a) (+ total a)

(+ a (+ a
(multiply-by-adding- (multiply-by- (multiply-by-adding- (multiply-by-adding-
helper adding-helper helper helper
a a (+ total a) (+ total a)
(- now 1) (+ now 1) (- now 1) (+ now 1)
) ) ) )
) ) ) )
) ) ) )
) )

(multiply-by-adding- (multiply-by- (multiply-by-adding- (multiply-by-adding-


helper adding-helper helper helper
a a 0 0
b 1 b 1
) ) ) )

) ) ) )

WORK. Study the versions, looking for and describing their similarities and differences. To
help your studying, insert display statements, carefully placing them so that they do not interfere
with the functioning of the procedures. In particular for each version see:
• how it starts
• what “are we done?” test is made
• what changes at each new recursive call
• what gets returned.
Example.

Here is another, slightly different version.

RD
running totals; down from n to 1 by 1
but tricky
(define (multiply-by-adding-DR
a
b
)

(define (multiply-by-adding-helper
total
now
)

(if
(= now 2)
(+ total a)

(multiply-by-adding-helper
(+ total a)
(- now 1)
)
)
)

(multiply-by-adding-helper
a
b
)
)

Notice how I used whitespace to focus your attention. Compare that with the squashed form
below.

(define (multiply-by-adding-DR a b)(define (multiply-by-adding-


helper total now) (if (= now 2)(+ total a)(multiply-by-adding-
helper (+ total a) (- now 1) )))(multiply-by-adding-helper ab))

WORK. Add displays and execute this procedure, deciding if it always works correctly? Study
the bold lines. What are the pros and cons of this version compared to the other ones?
Part III
Ruminations about a set of procedures all producing
the same answer.
This section ruminates on eight procedures that each compute factorial. Of course you know
what factorial is; the purpose here is to think in a wizardly way about how to compute it.

The factorial of n is the product of the numbers from 1 to n. To represent factorial you use this
shorthand notation:
n!
You show the computing needed for factorial by using the formula:
1 * 2 * ... * n-1 * n.
So, if n is 5 then n! is 1*2*3*4*5 = 120. Equivalently, this recurrence relation defines factorial:
factorial (1) = 1.
factorial (n) = n * factorial (n-1)
The first version of factorial, shown below, has several notable features. First, it is recursive,
since factorial is called by factorial. (Each version will be recursive.) Second, it uses the
"deferred computation" template. You know this to be the case since the factorial of n-1 is the
second expression to a multiplication in the if's alternative subexpression. Third, you can think
of it computing “Down” (or "backwards"), more specifically, using the " down from n by 1 to 1"
template in which the argument n decreases by 1 with each recursive call of factorial and the
recursion stops with a terminal return value 1 when n is 1. Compare this procedure to the DD
example in Part II. Do likewise for the procedures which follow.

Because of the deferred computation the actual order of multiplying is 1 times 2 times 3, etc.
However, if you ask for 5! the debug displays produce "54321" – be sure you understand why.

;; DD template
(define (factorial n)
(display n)
(if (= n 1)
1
(* n (factorial (- n 1)))
)
)
The second version of factorial, shown below, also has several notable features. First, while
factorial itself is not recursive, it uses a "helper" routine called "fac-helper", which is recursive.
(SICP often uses “iter”, short for “iterative”, as a suffix for a helper routine). Because fac-helper
is defined inside factorial, you say you are using block-structured template. Fac-helper is
recursive and has two arguments named product and counter. Factorial simply calls fac-helper
with product and counter both initially set to 1. Second, it uses the "running totals" template,
where product carries the running total. You know this to be the case since the if's alternative
subexpression has a call to fac-helper with some arithmetic to compute the arguments. Third,
you can think of it computing "forwards" or using the "up from 1 by 1 to n" template in that the
argument counter increases by 1 with each recursive call of fac-helper and the recursion stops
with a terminal return value of product, which is n!, when counter is the same as n. Fourth, n is
not an argument to fac-helper. It is available to fac-helper because fac-helper is defined within
factorial. (Be sure to compare and contrast counting up versus counting down.) Fifth, fac-helper
is invisible outside of factorial – it cannot be called by other procedures. Sixth, because of the
multiplication in forming the first argment to fac-helper, the actual order of multiplying is 1
times 2 times 3, etc, as above. For 5! the debug displays would produce "12345."

;; Block Structured RU template


(define (factorial n)
(define (fac-helper product counter)
(display counter)
(if (> counter n)
product
(fac-helper (* counter product) (+ counter 1) )
)
)
(fac-helper 1 1)
)
The third version of factorial, shown below, differs from the second version in that fac-helper is
not defined inside factorial. Therefore, unlike version two, n must be a third argument to fac-
helper. So, this fac-helper could be called by other procedures, but that isn’t very likely.

;; not Block Structured RU template


(define (factorial n)
(fac-helper 1 1 n)
)

(define (fac-helper product counter n)


(display counter)
(if (> counter n)
product
(fac-helper (* counter product) (+ counter 1) n)
)
)

The fourth version of factorial, shown below, differs from the second version in that it uses the
"from n down by 1 to 1" template rather than the "from 1 up by 1 to n" template.

;; Block Structured RD template


(define (factorial n)
(define (fac-helper product counter)
(display counter)
(if (= counter 1)
product
(fac-helper (* counter product) (- counter 1))
)
)
(fac-helper 1 n)
)
Work. The table shown below puts versions 2 and 4 side by side. Before reading on, study
them, looking for their similarities and differences.

(define (factorial n) (define (factorial n)


(define (fac-helper product counter) (define (fac-helper product counter)
(display counter) (display counter)
(if (> counter n) (if (= counter 1)
product product
(fac-helper (* counter product) (+ counter 1)) (fac-helper (* counter product) (- counter 1))
) )
) )
(fac-helper 1 1) (fac-helper 1 n)
) )
Version 2 Version 4

Notice how “1” and “n” are interchanged, how “-” replaces “+”, and how “=” replaces “>”.
The fifth version of factorial, shown below, differs from the fourth version in its test and
terminal return. Note that versions four and five never multiply by 1, which would have no effect
on the result, and have a probably - undetectable performance improvement, that is gained at the
cost of a somewhat harder to understand procedure. In other words, it distorts the template.

Note that the actual order of multiplying is n times n-1 times n-2 ... times 2.

(define (factorial n)
(define (fac-helper product counter)
(display counter)
(if (= counter 2)
(* 2 product)
(fac-helper (* counter product) (- counter 1))
)
)
(fac-helper 1 n)
)

Work. What happens if you execute (factorial 1)?

The sixth version of factorial, shown below, is like the first version in that it uses deferred
computation. However, it requires a helper routine in order to use a "from 1 up to n by 1"
template.

(define (factorial n)
(define (fac-helper counter)
(display counter)
(if (= counter n)
n
(* counter (fac-helper (+ counter 1))
)
)
)
(fac-helper 1)
)
The seventh version of factorial, shown below, is like the sixth except that fac-helper is not
defined within factorial, and so has a second argument, n. Compare it with version three.

(define (factorial n)
(fac-helper 1 n)
)
(define (fac-helper counter n)
(display counter)
(if (= counter n)
n
(* counter (fac-helper (+ counter 1) n))
)
)

The eighth version of factorial, shown below, differs from the second and third versions in that
fac-helper does not carry the product, a running total, as an argument. Instead, product is defined
inside factorial and modified with a set! during each call to fac-helper, except the last one. The
actual order of multiplying is 1 times 2 times 3, etc, as above. I included version eight to show
you the range of possibilities. However, be warned: this is a fundamentally different procedure
than the other seven. “Set!” (pronounced “set bang”) does not get defined until page 220, and
you are not allowed to use it in your assignments until you get to Chapter 3. “Begin” also gets
defined on page 220 in SICP. When you use “set!”, you often will also use begin — be sure,
eventually, to understand why that is so.

(define (factorial n)
(define product 1)
(define (fac-helper counter)
(display counter)
(if (> counter n)
product
(begin (set! product (* counter product))
(fac-helper (+ counter 1))
)
)
)
(fac-helper 1)
)
Work. Here are several exercises for you to accomplish on your road to wizardry.

A. Using the table below, study the similarities and differences among the versions of factorial:
• Of course, they all get the same result.
• All but the first have a helper routine.
• They have between one and three arguments to factorial or a helper procedure. The
arguments may be a counter, a running total or a terminal test value, n.
• They differ in the counter test value: 1, 2 or n.
• They differ in the terminal return value: 1, 2*product, or product.
• They differ in the order of actual multiplication.
• They differ in whether they multiply by 1 or not.

B. Study the layout style of the factorial versions, and judge whether it promotes easy reading.
Notice that the opening and closing parens of the define, if, and begin expressions line up
vertically. Other paren pairs appear on the same line. The subexpressions of each if and begin
line up vertically.

C. Add displays of “product” to versions where you can, write down what you expect to be
displayed, execute each version, and then compare what you expected with that the interpreter
produced.

D. For each version, modify it to calculate:


• The product from k to n. [Challenge: discover a special case and take advantage of it.]
• The product of even integers from 1 to n.
• The product of integers from 1 to n, except some k.
• The sum from 1 to n.
• The sum from 0 to n-1.

E. For each version, modify it to detect and appropriately deal with n being less than 1. You
decide what “appropriately” means.

F. For each version, modify it so that it is slightly wrong. You decide what slightly wrong
means. To justify this seemingly-perverse task, pretend that your modification is a test question,
and then write up an answer key suitable for other students.

G. Invent several more versions of factorial.

H. Mimic these ruminations for Fibonacci numbers (see page 37 in SICP).


(define (factorial n) (define (factorial n)
(display n) (fac-helper 1 1 n)
(if (= n 1) )
1
(* n (factorial (- n 1))) (define (fac-helper product counter n)
) (display counter)
) (if (> counter n)
product
(fac-helper (* counter product) (+ counter 1) n)
)
)
Version 1 Version 3
(define (factorial n) (define (factorial n)
(define (fac-helper product counter) (define (fac-helper product counter)
(display counter) (display counter)
(if (> counter n) (if (= counter 1)
product product
(fac-helper (* counter product) (+ counter 1)) (fac-helper (* counter product) (- counter 1))
) )
) )
(fac-helper 1 1) (fac-helper 1 n)
) )
Version 2 Version 4
(define (factorial n) (define (factorial n)
(define (fac-helper product counter) (fac-helper 1 n)
(display counter) )
(if (= counter 2) (define (fac-helper counter n)
(* 2 product) (display counter)
(fac-helper (* counter product) (- counter 1)) (if (= counter n)
) n
) (* counter (fac-helper (+ counter 1) n))
(fac-helper 1 n) )
) )
Version 5 Version 7
(define (factorial n) (define (factorial n)
(define (fac-helper counter) (define product 1)
(display counter) (define (fac-helper counter)
(if (= counter n) (display counter)
n (if (> counter n)
(* counter (fac-helper (+ counter 1))) product
) (begin (set! product (* counter product))
) (fac-helper (+ counter 1))
(fac-helper 1) )
) )
)
(fac-helper 1)
)
Version 6 Version 8
Part IV
Oops! The value of deliberate errors

Well, in previous Parts you have seen my examples of some templates, but just as seeing a ballet
does not make you a ballerina, you do not yet own them 1. As a template-owning wizard you will
make fewer mistakes than a novice. Moreover, when you do err you will have the problem
solving skills needed to debug. You will be quick because you are drawing from a bag full of
mastered tricks and will not have so many adventures. Instead of saying “huh?” you will say
“I’ve seen something like this before.” You also will know when to go slowly, pausing to check
where you are more likely to make mistakes. (Sod layers chant “green side up.”  ) This
mastery becomes even more important, for example, when the projects you take on become
larger, and when you maintain existing code (because of changed requirements) or re-use code
for a structurally similar purpose.

To acquire a wizard’s mastery you, of course need to use each template a few dozen (hundred?)
times – experience is crucial. What is different here is that I’m suggesting that you stress each
template, that is, deliberately make errors and then study what happens.

Warning! As you look at what follows you could conclude “Yikes! That’s really stupid” and
think you would never be that stupid. Sorry, we all are fallible, but while indeed some problems
are not your fault (bad documentation misinformed you, changed O/S behaves differently, a gust
of cosmic gravity flipped a zero to a one, etc.), it is almost always your fault. Some novices
never progress because they persist in trying to prove that they are not at fault. Wizards,
however, focus on solving problems, not on avoiding blame.

Below is a copy of the second version of factorial, with line numbers added for convenience.
Below it are a number of errors for you to deliberately make. To get the most out this
experience:
• Decide what values of n will make a good test set. [My list is below. Don’t look at it until
you make your own. Making test cases is an important wizardly skill.] Apply the test set
each time you change the code.
• Before executing the erroneous code, guess what will happen, and then compare what
happens with your expectations.
• Come up with a verbal description of the error that you made, that is, create names for
kinds of errors. Do not underestimate the value of a lexicon. Expect to build lexicons and
become skilled at precisely naming things, and enjoy the nuances of language.

1
Instead of “own” you could say “master” or “internalize.”
1. (define (factorial n)
2. (define (fac-helper product counter)
3. (display counter)
4. (if (> counter n)
5. product
6. (fac-helper (* counter product) (+ counter 1))
7. )
8. )
9. (fac-helper 1 1)
10. )
Here are some simple typos.
• Replace the second line with “(define (fax-helper product counter)”
• Replace the second line with “(define (fac-helper produck counter)”
• Replace the second line with “(define (fac-helper product ccounter)”

• Replace the fourth line with “(if (= counter n)”


• Replace the fourth line with “(if (< counter n)”

• Replace “+” with “-“

• Replace the ninth line with “(fac-helper 0 0)”

• Remove each line, one at a time.

• Replace the fourth line with “(if (> counter m)”


• Outside of factorial define m, then replace the fourth line with “(if (> counter m)”
• Outside of factorial define xxx to be “one” and then execute “(factorial xxx)”

Here are some simple out-of-order errors.


• Replace the second line with “(define (fac-helper counter product)”
• Replace the second line with “(fac-helper (+ counter 1) (* counter product)))“
• Interchange lines eight and nine.
• Interchange lines nine and ten.
• Move line 9 to after line 1.

Here are some paren errors to make.


• Replace the second line with “(define fac-helper product counter”
• Replace line 5 with “(product)”

• Add an extra “(“ in a dozen different places, one at a time.


• Add an extra “)“ in a dozen different places, one at a time.
• Add an extra pair of parens, “(“ and “)”, in a dozen different places, one at a time.
• Remove a “(“ in a dozen different places, one at a time.
• Remove a “)“ in a dozen different places, one at a time.
• Remove a pair of parens, “(“ and “)”, in a dozen different places, one at a time.

You will be more likely to make paren errors if you take out white space, as I did below.

(define (factorial n) (define (fac-helper product counter) (display counter)


(if (> counter n) product (fac-helper (* counter product) (+ counter 1)))) (fac-helper 1 1))

Here is my list of values for n: 5, 1, 0, -1, and 3.14. If you apply them each time you make
changes to the code, you call these “regression tests”. You apply them to check that your fixes
did not introduce new errors.

Why this list? “5” is a not-too-large, likely value for n – a normal case. “1” is the lowest legal
value specified in the recurrence relation – an extreme case. “0 is just beyond the extreme low
legal value. “-1” there since I remembered that integers are positive, zero or negative, and I
needed to cover the latter case. “3.14” is there to test non-integers – or just pi in the sky .

Here is an incomplete list of what might happen when you make one of these errors. Beware!
Different Scheme interpreters might respond to an error differently. [So, to increase your
mastery, you could make each error while running each interpreter.]

• Nothing – Scheme is waiting for you to do something else. Check for a missing “)”.
• Scheme squawks, printing an error message. The message makes the error obvious, you
fix it (and apply your regression tests) and life can resume.
• Scheme squawks, printing an error message. The message doesn’t help. You check your
work, and/or you put in displays and try again.
• You get an answer back but it is wrong. You check your work, and/or you put in displays
and try again.

WORK. Start a programming portfolio, where you keep annotated hunks of code. Toss in
hunks as you get them — your own or those written by others. Occasionally reorganize your
portfolio, inventing and articulating organizing principles. Be sure to have an “other” category.
Its hunks will induce you to see new organizing principles.
Part V
Defensive Programming
Study the first and second versions of factorial, copied below, and determine what happens if
they are evaluated with a parameter of zero or -1?

;; DD template ;; Block Structured RD template


(define (factorial n) (define (factorial n)
(display n) (define (fac-helper product counter)
(if (= n 1) (display counter)
1 (if (= counter 1)
(* n (factorial (- n 1))) product
) (fac-helper (* counter product)
) (- counter 1))
)
)
(fac-helper 1 n)
)
Version 1 Version 2
Sadly, both loop indefinitely (aka an “infinite” loop). The problem resides in the test for
equality, “(= n 1)” – it is never true, so the recursion continues. This clearly is bad. To fix it
requires defensive programming.

What should happen? Well, the factorial recurrence relation is silent about an n of zero or minus
one – or for 2.1, or “”, or anything other than a positive integer. You need to decide – and
document your decisions. Version 1 is a bit defensive, since it has a test “<=” and so works for
any integer, but does not warn the user that she/he was bad. Without a helper procedure there is
no place in version 1a to insert other defensive code.
;; DD template
(define (factorial n)
(display n)
(if (<= n 1)
1
(* n (factorial (- n 1)))
)
)

Version 1a
Version 2a is even more defensive. It checks for non-integer and non-positive integer values, and
does so before calling fac-helper.

;; Block Structured RD template


(define (factorial n)
(define (fac-helper product counter)
(display counter)
(if (= counter 1)
product
(fac-helper (* counter product) (- counter 1))
)
)
(cond
((not (integer? n)) (begin (display “bad factorial n – not integer”)
0))
((< n 1) (begin (display “bad factorial n – not positive integer”)
0))
(else (fac-helper 1 n))
)
)
Version 2a
What do you think about the warning messages, and the decision to return zero?

You might also like