Unfortunately, the standard recipe is not good enough for the design of
algorithms. Up to now, a function has always produced an output for any
legitimate input. That is, the evaluation has always stopped. After all,
by the nature of our recipe, each natural recursion consumes an immediate
piece of the input, not the input itself. Because data is constructed in
a hierarchical manner, this means that the input shrinks at every
stage. Hence, the function sooner or later consumes an atomic piece of
data and stops.
With functions based on generative recursion, this is no longer true. The
internal recursions don't consume an immediate component of the input but
some new piece of data, which is generated from the input. As
exercise~#exmoveuntil1#33667> shows, this step may produce the input over
and over again and thus prevent the evaluation from ever producing a
result. We say that the program <#66246#><#33668#>LOOPS<#33668#><#66246#> or is in an
<#66247#><#33669#>INFINITE LOOP<#33669#><#66247#>.
In addition, even the slightest mistake in translating the process
description into a function definition may cause an infinite loop. The
problem is most easily understood with an example. Consider the following
definition of <#66248#><#33670#>smaller-items<#33670#><#66248#>, one of the two ``problem
generators'' for <#66249#><#33671#>quick-sort<#33671#><#66249#>:
<#71469#>;; <#66250#><#33676#>smaller-items<#33676#> <#33677#>:<#33677#> <#33678#>(listof<#33678#> <#33679#>number)<#33679#> <#33680#>number<#33680#> <#33681#><#33681#><#33682#>-;SPMgt;<#33682#><#33683#><#33683#> <#33684#>(listof<#33684#> <#33685#>number)<#33685#><#66250#><#71469#>
<#71470#>;; to create a list with all those numbers on <#66251#><#33686#>alon<#33686#><#66251#> <#71470#>
<#71471#>;; that are smaller than or equal to <#66252#><#33687#>threshold<#33687#><#66252#><#71471#>
<#33688#>(d<#33688#><#33689#>efine<#33689#> <#33690#>(smaller-items<#33690#> <#33691#>alon<#33691#> <#33692#>threshold)<#33692#>
<#33693#>(c<#33693#><#33694#>ond<#33694#>
<#33695#>[<#33695#><#33696#>(empty?<#33696#> <#33697#>alon)<#33697#> <#33698#>empty]<#33698#>
<#33699#>[<#33699#><#33700#>else<#33700#> <#33701#>(if<#33701#> <#33702#>(;SPMlt;=<#33702#> <#33703#>(first<#33703#> <#33704#>alon)<#33704#> <#33705#>threshold)<#33705#>
<#33706#>(cons<#33706#> <#33707#>(first<#33707#> <#33708#>alon)<#33708#> <#33709#>(smaller-items<#33709#> <#33710#>(rest<#33710#> <#33711#>alon)<#33711#> <#33712#>threshold))<#33712#>
<#33713#>(smaller-items<#33713#> <#33714#>(rest<#33714#> <#33715#>alon)<#33715#> <#33716#>threshold))]<#33716#><#33717#>))<#33717#>
Instead of <#66253#><#33721#>;SPMlt;<#33721#><#66253#> it employs <#66254#><#33722#>;SPMlt;=<#33722#><#66254#> to compare numbers. As a
result, this function produces <#66255#><#33723#>(list<#33723#>\ <#33724#>5)<#33724#><#66255#> when applied to
<#66256#><#33725#>(list<#33725#>\ <#33726#>5)<#33726#><#66256#> and <#66257#><#33727#>5<#33727#><#66257#>.
Worse, if the <#66258#><#33728#>quick-sort<#33728#><#66258#> function from figure~#figqsort#33729> is
combined with this new version of <#66259#><#33730#>smaller-items<#33730#><#66259#>, it doesn't produce
any output for <#66260#><#33731#>(list<#33731#>\ <#33732#>5)<#33732#><#66260#>:
<#33737#>(quick-sort<#33737#> <#33738#>(list<#33738#> <#33739#>5))<#33739#>
<#33740#>=<#33740#> <#33741#>(append<#33741#> <#33742#>(quick-sort<#33742#> <#33743#>(smaller-items<#33743#> <#33744#>5<#33744#> <#33745#>(list<#33745#> <#33746#>5)))<#33746#>
<#33747#>(list<#33747#> <#33748#>5)<#33748#>
<#33749#>(quick-sort<#33749#> <#33750#>(larger-items<#33750#> <#33751#>5<#33751#> <#33752#>(list<#33752#> <#33753#>5))))<#33753#>
<#33754#>=<#33754#> <#33755#>(append<#33755#> <#72345#>#tex2html_wrap_inline73616#<#72345#>
<#33759#>(list<#33759#> <#33760#>5)<#33760#>
<#33761#>(quick-sort<#33761#> <#33762#>(larger-items<#33762#> <#33763#>5<#33763#> <#33764#>(list<#33764#> <#33765#>5))))<#33765#>
The first recursive use demands that <#66262#><#33769#>quick-sort<#33769#><#66262#> solve the problem
of sorting <#66263#><#33770#>(list<#33770#>\ <#33771#>5)<#33771#><#66263#>---but that is the exact problem that we
started from. Since this is a circular evaluation, <#66264#><#33772#>(quick-sort<#33772#><#33773#> <#33773#><#33774#>(list<#33774#>\ <#33775#>5))<#33775#><#66264#> never produces a result. More generally, there is no guarantee
that the size of the input for a recursive call brings us closer to a
solution than the original input.
The lesson from this example is that the design of algorithms requires one
more step in our design recipe: a <#66265#><#33776#>TERMINATION ARGUMENT<#33776#><#66265#>, which
explains why the process produces an output for every input and how the
function implements this idea; or a <#66266#><#33777#>TERMINATION WARNING<#33777#><#66266#>, which
explains when the process may not terminate.
For <#66267#><#33778#>quick-sort<#33778#><#66267#>, the argument might look like this:
At each step, <#66268#><#33780#>quick-sort<#33780#><#66268#> partitions the list into two sublists
using <#66269#><#33781#>smaller-items<#33781#><#66269#> and <#66270#><#33782#>larger-items<#33782#><#66270#>. Each function
produces a list that is smaller than the input (the second argument), even
if the threshold (the first argument) is an item on the list. Hence, each
recursive application of <#66271#><#33783#>quick-sort<#33783#><#66271#> consumes a strictly shorter
list than the given one. Eventually, <#66272#><#33784#>quick-sort<#33784#><#66272#> receives
and returns <#66273#><#33785#>empty<#33785#><#66273#>.
Without such an argument an algorithm must be considered incomplete.
A good termination argument may on occasion also reveal additional
termination cases. For example, <#66274#><#33787#>(smaller-items<#33787#>\ <#33788#>N<#33788#>\ <#33789#>(list<#33789#>\ <#33790#>N))<#33790#><#66274#> and
<#66275#><#33791#>(larger-items<#33791#>\ <#33792#>N<#33792#>\ <#33793#>(list<#33793#>\ <#33794#>N))<#33794#><#66275#> always produce <#66276#><#33795#>empty<#33795#><#66276#> for any
<#66277#><#33796#>N<#33796#><#66277#>. Therefore we know that <#66278#><#33797#>quick-sort<#33797#><#66278#>'s answer for
<#66279#><#33798#>(list<#33798#>\ <#33799#>N)<#33799#><#66279#> is <#66280#><#33800#>(list<#33800#>\ <#33801#>N)<#33801#><#66280#>. To add this
knowledge to <#66282#><#33803#>quick-sort<#33803#><#66282#>, we simply add a <#66283#><#33804#>cond<#33804#><#66283#>-clause:
<#33809#>(d<#33809#><#33810#>efine<#33810#> <#33811#>(quick-sort<#33811#> <#33812#>alon)<#33812#>
<#33813#>(c<#33813#><#33814#>ond<#33814#>
<#33815#>[<#33815#><#33816#>(empty?<#33816#> <#33817#>alon)<#33817#> <#33818#>empty]<#33818#>
<#33819#>[<#33819#><#33820#>(empty?<#33820#> <#33821#>(rest<#33821#> <#33822#>alon))<#33822#> <#33823#>alon]<#33823#>
<#33824#>[<#33824#><#33825#>else<#33825#> <#33826#>(a<#33826#><#33827#>ppend<#33827#>
<#33828#>(quick-sort<#33828#> <#33829#>(smaller-items<#33829#> <#33830#>alon<#33830#> <#33831#>(first<#33831#> <#33832#>alon)))<#33832#>
<#33833#>(list<#33833#> <#33834#>(first<#33834#> <#33835#>alon))<#33835#>
<#33836#>(quick-sort<#33836#> <#33837#>(larger-items<#33837#> <#33838#>alon<#33838#> <#33839#>(first<#33839#> <#33840#>alon))))]<#33840#><#33841#>))<#33841#>
The condition <#66284#><#33845#>(empty?<#33845#>\ <#33846#>(rest<#33846#>\ <#33847#>alon))<#33847#><#66284#> is one way to ask the question
whether <#66285#><#33848#>alon<#33848#><#66285#> contains one item.
<#33851#>Exercise 26.1.1<#33851#>
Define the function <#66286#><#33853#>tabulate-div<#33853#><#66286#>, which accepts a number <#66287#><#33854#>n<#33854#><#66287#>
and tabulates the list of all of its divisors, starting with
<#66288#><#33855#>1<#33855#><#66288#> and ending in <#66289#><#33856#>n<#33856#><#66289#>. A number <#66290#><#33857#>d<#33857#><#66290#> is a divisior of a
number <#66291#><#33858#>n<#33858#><#66291#> if the remainder of dividing <#66292#><#33859#>n<#33859#><#66292#> by <#66293#><#33860#>d<#33860#><#66293#> is
<#66294#><#33861#>0<#33861#><#66294#>, that is, <#66295#><#33862#>(=<#33862#>\ <#33863#>(remainder<#33863#>\ <#33864#>n<#33864#>\ <#33865#>d)<#33865#>\ <#33866#>0)<#33866#><#66295#> is true. The smallest
divisior of any number is <#66296#><#33867#>1<#33867#><#66296#>; the largest one is the number
itself.~ Solution<#66297#><#66297#>
<#33873#>Exercise 26.1.2<#33873#>
Develop the function <#66298#><#33875#>merge-sort<#33875#><#66298#>, which sorts a list of numbers in
ascending order, using the following two auxiliary functions:
- The first one, <#66299#><#33877#>make-singles<#33877#><#66299#>, constructs a list of
one-item lists from the given list of numbers. For example,
<#33882#>(make-singles<#33882#> <#33883#>(list<#33883#> <#33884#>2<#33884#> <#33885#>5<#33885#> <#33886#>9<#33886#> <#33887#>3))<#33887#>
<#33888#>=<#33888#> <#33889#>(list<#33889#> <#33890#>(list<#33890#> <#33891#>2)<#33891#> <#33892#>(list<#33892#> <#33893#>5)<#33893#> <#33894#>(list<#33894#> <#33895#>9)<#33895#> <#33896#>(list<#33896#> <#33897#>3))<#33897#>
- The second one, <#66300#><#33901#>merge-all-neighbors<#33901#><#66300#>, merges pairs of neighboring
lists. More specifically, it consumes a list of lists (of numbers) and
merges neighbors. For example,
<#33906#>(merge-all-neighbors<#33906#> <#33907#>(list<#33907#> <#33908#>(list<#33908#> <#33909#>2)<#33909#> <#33910#>(list<#33910#> <#33911#>5)<#33911#> <#33912#>(list<#33912#> <#33913#>9)<#33913#> <#33914#>(list<#33914#> <#33915#>3)))<#33915#>
<#33916#>=<#33916#> <#33917#>(list<#33917#> <#33918#>(list<#33918#> <#33919#>2<#33919#> <#33920#>5)<#33920#> <#33921#>(list<#33921#> <#33922#>3<#33922#> <#33923#>9))<#33923#>
<#33924#>(merge-all-neighbors<#33924#> <#33925#>(list<#33925#> <#33926#>(list<#33926#> <#33927#>2<#33927#> <#33928#>5)<#33928#> <#33929#>(list<#33929#> <#33930#>3<#33930#> <#33931#>9)))<#33931#>
<#33932#>=<#33932#> <#33933#>(list<#33933#> <#33934#>(list<#33934#> <#33935#>2<#33935#> <#33936#>3<#33936#> <#33937#>5<#33937#> <#33938#>9))<#33938#>
In general, this function yields a list that is approximately half as long as
the input. Why is the output not always half as long as the input?
Make sure to develop the functions independently.
The function <#66301#><#33943#>merge-sort<#33943#><#66301#> first uses <#66302#><#33944#>make-singles<#33944#><#66302#> to create
a list of single lists; then it relies on <#66303#><#33945#>merge-all-neighbors<#33945#><#66303#> to
shorten the list of lists until it contains a single list. The latter is
the result.~ Solution<#66304#><#66304#>