![[index]](../icons/index.gif)
Next: Termination
Up: Generative Recursion
Previous: Sorting Quickly
At first glance, the algorithms move-until-out and
quick-sort have little in common. One processes structures; the
other processes lists. One creates a new structure for the generative step;
the other splits up a list into three pieces and recurs on two of them. In
short, a comparison of the two examples of generative recursion suggests
that the design of algorithms is an ad hoc activity and that it is
impossible to come up with a general design recipe. A closer look, however,
suggests a different picture.
First, even though we speak of algorithms as processes that solve problems,
they are still functions that consume and produce data. In other words, we
still choose data to represent a problem, and we must definitely understand
the nature of our data if we wish to understand the process. Second, we
describe the processes in terms of data, for example, ``creating a new
structure'' or ``partitioning a list of numbers.'' Third, we always
distinguish between input data for which it is trivial to produce a
solution and those for which it is not. Fourth, the generation of problems
is the key to the design of algorithms. Although the idea of how to
generate a new problem might be independent of a data representation, it
must certainly be implemented for whatever form of representation we choose
for our problem. Finally, once the generated problems have been solved, the
solutions must be combined with other values.
Let us examine the six general stages of our structural design recipe in
light of our discussion:
- Data analysis and design:
- The choice of a data representation for
a problem often affects our thinking about the process. Sometimes the
description of a process dictates a particular choice of representation. On
other occasions, it is possible and worthwhile to explore alternatives. In
any case, we must analyze and define our data collections.
- Contract, purpose, header:
- We also need a contract, a definition
header, and a purpose statement. Since the generative step has no
connection to the structure of the data definition, the purpose statement
should not only specify what the function does but should also
include a comment that explains in general terms how the it works.
- Function examples:
- In our previous design recipes, the function
examples merely specified which output the function should produce for some
given input. For algorithms, examples should illustrate how the
algorithm proceeds for some given input. This helps us to design, and
readers to understand, the algorithm. For functions such as
move-until-out the process is trivial and doesn't need more than a
few words. For others, including, quick-sort, the process relies
on a non-trivial idea for its generative step, and its explanation requires
a good example such as the one in figure
.
- Template:
- Our discussion suggests a general template for algorithms:
(define (generative-recursive-fun problem)
(cond
[(trivially-solvable? problem)
(determine-solution problem)]
[else
(combine-solutions
... problem ...
(generative-recursive-fun (generate-problem-1 problem))
(generative-recursive-fun (generate-problem-n problem)))]))
- Definition:
- Of course, this template is only a suggestive blueprint,
not a definitive shape. Each function in the template is to remind us that
we need to think about the following four questions:
- What is a trivially solvable problem?
- What is a corresponding solution?
- How do we generate new problems that are more easily solvable
than the original problem? Is there one new problem that we generate
or are there several?
- Is the solution of the given problem the same as the solution of
(one of) the new problems? Or, do we need to combine the solutions to
create a solution for the original problem? And, if so, do we need
anything from the original problem data?
To define the algorithm, we must express the answers to these four
questions in terms of our chosen data representation.
- Test:
- Once we have a complete function, we must also test it. As
before, the goal of testing is to discover bugs and to eliminate them.
Remember that testing cannot validate that the function works correctly
for all possible inputs. Also remember that it is best to formulate tests
as boolean-valued expressions that automatically compare the expected value
with the computed value (see section
).
Exercises
Exercise 26.0.1
Formulate informal answers to the four key questions for the problem of
modeling a ball's movement across a canvas until it is out of bounds.
Solution
Exercise 26.0.2
Formulate informal answers to the four key questions for the
quick-sort problem. How many instances of
generate-problem are there? Solution
![[index]](../icons/index.gif)
Next: Termination
Up: Generative Recursion
Previous: Sorting Quickly
PLT