The function development in the preceding section suggests some amendments to our design recipe. Specifically, the data analysis step, the template construction step, and the definition of the function's body require adjustments.
The example of the preceding section deals with two distinct kinds of shapes, each of which has several properties. We captured this idea with the following data definition:
A shape is either
- a circle structure:
(make-circle p s)
where p is a posn and s is a number; or- a square structure:
(make-square p s)
where p is a posn and s is a number.
It specifies that every shape belongs to one of two subclasses of data.
For a data definition to make sense, it must be possible to formulate conditions that distinguish the various subclasses in a definition. That is, if x stands for a piece of data in the defined class, we must be able to use built-in and user-defined predicates to distinguish the enumerated subclasses from each other. In our running example, the two conditions would be (square? x) and (circle? x).
Here is the template for our running example:
;; f : shape -> ???
(define (f a-shape)
(cond
[(square? a-shape) ...]
[(circle? a-shape) ...]))
The output specification and the purpose statement are missing to emphasize that a template has no connection to the output or the purpose of a function.
Once we have formulated the template for the conditional, we refine the template further, cond-line by cond-line. If the purpose of a line is to process atomic information, we are done. If a line processes compound data, we enrich the template with appropriate selector expressions.
Let's illustrate the idea with our running example again:
(define (f a-shape)
(cond
[(square? a-shape)
... (square-nw a-shape) ... (square-length a-shape) ...]
[(circle? a-shape)
... (circle-center a-shape) ... (circle-radius a-shape) ...]))
Suppose we want to define a function that computes the perimeter of a shape. Then we start from the template and fill in the gaps:
;; perimeter : shape -> number
;; to compute the perimeter of a-shape
(define (perimeter a-shape)
(cond
[(square? a-shape) (* (square-length a-shape) 4)]
[(circle? a-shape) (* (* 2 (circle-radius a-shape)) pi)]))
Figure
summarizes the development of our running
example.
The figure is not yet translated into HTML. Figure: Designing a function for mixed data
Even a cursory comparative reading of the design recipes in
sections
,
,
, and the
current one suggests that the data analysis and the template design steps
are becoming more and more important. If we do not understand what kind of
data a function consumes, we cannot design it and organize it properly. If,
however, we do understand the structure of the data definition and organize
our template properly, it is easy to modify or to extend a function. For
example, if we add new information to the representation of a
circle, then only those cond-clauses related to circles
may require changes. Similarly, if we add a new kind of shape to our data
definition, say, rectangles, we must add new cond-clauses to our
functions.
Exercise 7.2.1
Develop structure and data definitions for a collection of zoo animals. The collection includes
Develop the function fits?. The function consumes a zoo animal and the volume of a cage. It determines whether the cage is large enough for the animal. Solution
Exercise 7.2.2
The administrators of metropolitan transportation agencies manage fleets of vehicles. Develop structure and data definitions for a collection of such vehicles. The collection should include at least buses, limos, cars, and subways. Add at least two attributes per class of vehicle.
Then develop a template for functions that consume vehicles.
Solution