Sections
through
suggest
that the design of functions for compound data proceeds in a regular
manner. First, a programmer must recognize that structures are needed. We
follow the simple rule of using structures whenever the description of some
object specifies several pieces of information. If we don't use structures
in these cases, we quickly lose track of which data belongs to which
object, especially when we write large functions that process massive
amounts of data.
Second, a programmer can use the structure and data definitions for the organization of a function. We use the term template when we design functions. As we will see in this and many future sections, the template matches the data definition, and the template is the essential step in the careful design of functions.
To emphasize this point, we modify our function design recipe from
section
to accommodate compound data. Most importantly,
working with compound data requires adjustments in a few of the basic
design steps and two new steps: data analysis and template design:
Until now we could use Scheme's classes of atomic data (numbers, symbols, images, etc.) to represent information. But they are not enough. If we discover that an object has N properties, we introduce a structure definition with N fields and supply a data definition that specifies what kind of data the fields may contain.
Let us consider functions that process student records at a school. If a student's interesting properties for a school are
(define-struct student (last first teacher))
Here is the data definition that specifies the class of student structures as precisely as possible:
A student is a structure:
(make-student l f t) where l, f, and t are symbols.
The corresponding data class contains structures like these:
(make-student 'findler 'kathi 'matthias) (make-student 'fisler 'sean 'matthias) (make-student 'flatt 'shriram 'matthias)
In other words, a template expresses what we know about the inputs, and nothing about the outputs. We can therefore use the same template for all functions that consume the same kind of structure. Also, because a template does not express anything about the purpose of the function, we can formulate it before or after we have developed examples.
Consider a function that consumes a student structure and a teacher name:
;; process-student : student symbol -> ??? (define (process-student a-student a-teacher) ...)Then a-student is a parameter that stands for a structure and a-teacher stands for just a symbol. The template therefore has the following shape:
;; process-student : student symbol -> ??? (define (process-student a-student a-teacher) ... (student-last a-student) ... ... (student-first a-student) ... ... (student-teacher a-student) ...)The ??? output reminds us that we don't assume anything about the output of the function. We design every function that consumes a student structure using this template.
(check (make-student 'Wilson 'Fritz 'Harper) 'Harper) ;; expected value: 'Wilson(check (make-student 'Wilson 'Fritz 'Lee) 'Harper) ;; expected value 'none
The second function, transfer, is supposed to produce a student structure that contains the same information as a-student except for the teacher field, which should be a-teacher:
(transfer (make-student 'Wilson 'Fritz 'Harper) 'Lee) ;; expected value: (make-student 'Wilson 'Fritz 'Lee)(transfer (make-student 'Woops 'Helen 'Flatt) 'Fisler) ;; expected value: (make-student 'Woops 'Helen 'Fisler)
Let us return to our first example, check:
(define (check a-student a-teacher)
(cond
[(symbol=? (student-teacher a-student) a-teacher)
(student-last a-student)]
[else 'none]))
This particular function uses two of the three selector expressions from the
template. Specifically, it compares the result of the selector expression
(student-teacher a-student) with a-teacher and, if they
are equal, produces the result of (student-last a-student). Just
naming the results of the selector expressions and reading the problem
statement makes the definition obvious.
Similarly, the transfer function is easy to define using the template and the examples:
(define (transfer a-student a-teacher)
(make-student (student-last a-student)
(student-first a-student)
a-teacher))
This second function uses the same two selector expressions as the first
example, but in a different way. The key observation, however, is that the
template reminds us of all the information that we have available. When we define the function, we must use and combine the available information.
The figure is not yet translated into HTML.
Exercise 6.5.1
Develop templates for functions that consume the following structures:
Exercise 6.5.2
Develop the function time->seconds, which consumes a time
structure (see exercise
) and produces the number of seconds
since midnight that the time structure represents.
Example:
(time->seconds (make-time 12 30 2)) ;; expected value: 45002Explain the example. Solution