Thank you very much for posting the solution to 2.40.
Here are the solutions to the exercises you requested:
(define (make-exponentiation b exp)
(cond ((=number? exp 0) 1)
((=number? exp 1) b)
(list '** b exp))))
(define (base z)
(define (exponent z)
Define the test:
(define (exponentiation? x)
(and (pair? x) (eq? (car x) '**)))
Note: this test could be improved to make shure '** gets exactly two arguments.
In the deriv procedure add the following condition:
(make-product (exponent exp)
(make-exponentiation (base exp)
(- (exponent exp) 1)))
(deriv (base exp) var)))
(a) Each division will have a private get-record operation, so each
division's package will look like this:
(define (get-record employee file)
(put 'get-record 'research get-record)
Then we can write a global get-record procedure like this:
(define (get-record employee division-file)
((get 'get-record (type-tag division-file))
It'll be invoked, for example, like this:
(get-record '(Alan Perlis) research-personnel-list)
For this to work, each division's file must include a type tag
specifying which division it is.
If, for example, a particular division file is a sequence of
records, one per employee, and if the employee name is the CAR of
each record, then that division can use ASSOC as its get-record
procedure, by saying
(put 'get-record 'manufacturing assoc)
in its package-installation procedure.
(b) The salary field might be in a different place in each
division's files, so we have to use the right selector based
on the division tag.
(define (get-salary record)
(apply-generic 'salary record))
Each division's package must include a salary selector, comparable
to the magnitude selectors in the complex number example.
Why did I use GET directly in (a) but apply-generic in (b)? In the
case of get-salary, the argument is a type-tagged datum. But in the
case of get-record, there are two arguments, only one of which (the
division file) has a type tag. The employee name, I'm assuming,
is not tagged.
(c) Find an employee in any division
(define (find-employee-record employee divisions)
(cond ((null? divisions) (error "No such employee"))
((get-record employee (car divisions)))
(else (find-employee-record employee (cdr divisions)))))
This uses the feature that a cond clause with only one expression
returns the value of that expression if it's not false.
(d) To add a new division, you must create a package for the division,
make sure the division's personnel file is tagged with the division name,
and add the division's file to the list of files used as argument to
To add a new operator:
First we must write a low-level procedure for that operator for each type,
like (magnitude-rectangular) and (magnitude-polar) if we're adding the
operator magnitude. (If we're using a package system, we can add a
local definition of MAGNITUDE to each package.) Then...
For conventional style, we write a generic operator procedure
(magnitude) that invokes the appropriate lower-level procedure
depending on the type of its operand.
For data-directed style, we use PUT to insert entries in the
procedure matrix for each low-level procedure; there is no new
high-level procedure required.
For message-passing style, we modify each of the type dispatch
procedures to accept a new message corresponding to the new
operator, dispatching to the appropriate low-level procedure.
To add a new type:
First we must write a low-level procedure for that type for each
operator, like (real-part-polar), (imag-part-polar),
(magnitude-polar), and (angle-polar) if we're inventing the
polar type. (If we're using a package system, we can create
a new POLAR package with local definitions of REAL-PART, etc.)
For conventional style, we modify each of the generic operator
procedures (real-part), (imaginary-part), etc. to know about the
new type, dispatching to the appropriate lower-level procedures.
For data-directed style, we use PUT to insert entries, as for
a new operator.
For message-passing style, we write a new type dispatch procedure
that accepts messages 'real-part, 'imag-part, etc. and dispatches
to the appropriate lower-level procedure.
Which is most appropriate:
Conventional style is certainly INappropriate when many new types
will be invented, because lots of existing procedures need to be
Similarly, message-passing is INappropriate when many new operators
will be invented and applied to existing types.
Data-directed programming is a possible solution in either case, and is
probably the best choice if both new types and new operators are likely.
It's also a good choice if the number of types or operators is large in
the first place, whether or not new ones will be invented, because it
minimizes the total number of procedures needed (leaving out the generic
dispatch procedures for each type or operator) and thereby reduces the
chances for error.
As you'll see in chapter 3, message-passing style takes on new importance
when a data object needs to keep track of local state. But you'll see
later in the chapter (mutable data) that there are other solutions to
the local state problem, too.
Message-passing is also sometimes sensible when there are lots of types,
each of which has its own separate set of operators with unique names, so
that a data-directed array would be mostly empty.
;: (put 'real-part '(complex) real-part)
;: (put 'imag-part '(complex) imag-part)
;: (put 'magnitude '(complex) magnitude)
;: (put 'angle '(complex) angle)
(magnitude '(complex rectangular 3 . 4))
we call MAGNITUDE giving
(apply-generic 'magnitude '(complex rectangular 3 . 4))
The apply-generic function (see pg. 184) just uses GET to find the
entry corresponding to 'magnitude and '(complex), and gets the same
function MAGNITUDE that we invoked in the first place. This
time, however, the argument to MAGNITUDE is (CONTENTS OBJ)
so that the first type flag (COMPLEX) is removed. In other
words, we end up calling
(magnitude '(rectangular 3 . 4))
Calling the function MAGNITUDE again, this becomes :
(apply-generic 'magnitude '(rectangular 3 . 4))
The apply-generic function now looks up the entry for 'magnitude and
'(rectangular) and finds the MAGNITUDE function from the RECTANGULAR
package; that function is called with '(3 . 4) as its argument, which
yields the final answer... (sqrt (square 3) (square 4)) ==> 5
One more thing, I don't know if all the solutions are in this book but
you might want to check out:
The Solutions Manual to The Structure and Interpretation of Programs
exercise number AND exercise text
Please let me know if you have anymore questions.