1 ;; Exercise 2.79. Define a generic equality predicate equ? that tests the equality of two numbers, and install it in the generic arithmetic package. This operation should work for ordinary numbers, rational numbers, and complex numbers.
3 ;; Exercise 2.80. Define a generic predicate =zero? that tests if its argument is zero, and install it in the generic arithmetic package. This operation should work for ordinary numbers, rational numbers, and complex numbers.
5 (define (attach-tag type-tag contents)
6 (if (eq? type-tag 'scheme-number)
8 (cons type-tag contents)))
9 (define (type-tag datum)
10 (cond ((number? datum) 'scheme-number)
11 ((pair? datum) (car datum))
12 ((error "error -- invalid datum" datum))))
13 (define (contents datum)
14 (cond ((number? datum) datum)
15 ((pair? datum) (cdr datum))
16 ((error "error -- invalid datum" datum))))
19 (define (assoc key records)
20 (cond ((null? records) false)
21 ((equal? key (caar records)) (car records))
22 (else (assoc key (cdr records)))))
23 (let ((local-table (list '*table*)))
24 (define (lookup key-1 key-2)
25 (let ((subtable (assoc key-1 (cdr local-table))))
27 (let ((record (assoc key-2 (cdr subtable))))
32 (define (insert! key-1 key-2 value)
33 (let ((subtable (assoc key-1 (cdr local-table))))
35 (let ((record (assoc key-2 (cdr subtable))))
37 (set-cdr! record value)
39 (cons (cons key-2 value)
47 (cond ((eq? m 'lookup-proc) lookup)
48 ((eq? m 'insert-proc!) insert!)
49 (else (error "Unknown operation -- TABLE" m))))
52 (define operation-table (make-table))
53 (define get (operation-table 'lookup-proc))
54 (define put (operation-table 'insert-proc!))
55 (define coercion-table (make-table))
56 (define get-coercion (coercion-table 'lookup-proc))
57 (define put-coercion (coercion-table 'insert-proc!))
59 (define (add x y) (apply-generic 'add x y))
60 (define (sub x y) (apply-generic 'sub x y))
61 (define (mul x y) (apply-generic 'mul x y))
62 (define (div x y) (apply-generic 'div x y))
63 (define (equ? x y) (apply-generic 'equ? x y))
64 (define (=zero? x) (apply-generic '=zero? x))
66 (define (install-scheme-number-package)
67 (define (tag x) (attach-tag 'scheme-number x))
68 (put 'add '(scheme-number scheme-number)
69 (lambda (x y) (tag (+ x y))))
70 (put 'sub '(scheme-number scheme-number)
71 (lambda (x y) (tag (- x y))))
72 (put 'mul '(scheme-number scheme-number)
73 (lambda (x y) (tag (* x y))))
74 (put 'div '(scheme-number scheme-number)
75 (lambda (x y) (tag (/ x y))))
76 (put 'equ? '(scheme-number scheme-number) =)
77 (put '=zero? '(scheme-number) zero?)
78 (put 'make 'scheme-number
82 (define (install-rational-package)
86 (gcd b (remainder a b))))
87 (define (numer x) (car x))
88 (define (denom x) (cdr x))
89 (define (make-rat n d)
91 (cons (/ n g) (/ d g))))
93 (make-rat (+ (* (numer x) (denom y))
94 (* (numer y) (denom x)))
95 (* (denom x) (denom y))))
97 (make-rat (- (* (numer x) (denom y))
98 (* (numer y) (denom x)))
99 (* (denom x) (denom y))))
100 (define (mul-rat x y)
101 (make-rat (* (numer x) (numer y))
102 (* (denom x) (denom y))))
103 (define (div-rat x y)
104 (make-rat (* (numer x) (denom y))
105 (* (denom x) (numer y))))
106 (define (equ-rat? x y)
107 (and (= (numer x) (numer y))
108 (= (denom x) (denom y))))
109 (define (=zero-rat? x) (= (numer x) 0))
110 (define (tag x) (attach-tag 'rational x))
111 (put 'add '(rational rational)
112 (lambda (x y) (tag (add-rat x y))))
113 (put 'sub '(rational rational)
114 (lambda (x y) (tag (sub-rat x y))))
115 (put 'mul '(rational rational)
116 (lambda (x y) (tag (mul-rat x y))))
117 (put 'div '(rational rational)
118 (lambda (x y) (tag (div-rat x y))))
119 (put 'equ? '(rational rational) equ-rat?)
120 (put '=zero? '(rational) =zero-rat?)
122 (lambda (n d) (tag (make-rat n d))))
125 (define (install-complex-package)
126 (define (make-from-real-imag x y)
127 ((get 'make-from-real-imag 'rectangular) x y))
128 (define (make-from-mag-ang r a)
129 ((get 'make-from-mag-ang 'polar) r a))
131 (define (real-part z) (apply-generic 'real-part z))
132 (define (imag-part z) (apply-generic 'imag-part z))
133 (define (magnitude z) (apply-generic 'magnitude z))
134 (define (angle z) (apply-generic 'angle z))
136 ;; rectangular and polar representations...
138 (define (install-complex-rectangular)
139 (define (make-from-real-imag-rectangular x y)
141 (define (make-from-mag-ang-rectangular r a)
142 (cons (* r (cos a)) (* r (sin a))))
143 (define (real-part z) (car z))
144 (define (imag-part z) (cdr z))
145 (define (magnitude z)
146 (sqrt (+ (square (real-part z))
147 (square (imag-part z)))))
148 (define (angle z) (atan (imag-part z) (real-part z)))
149 (define (tag x) (attach-tag 'rectangular x))
150 (put 'real-part '(rectangular) real-part)
151 (put 'imag-part '(rectangular) imag-part)
152 (put 'magnitude '(rectangular) magnitude)
153 (put 'angle '(rectangular) angle)
154 (put 'make-from-real-imag 'rectangular
155 (lambda (x y) (tag (make-from-real-imag-rectangular x y))))
156 (put 'make-from-mag-ang 'rectangular
157 (lambda (r a) (tag (make-from-mag-ang-rectangular r a))))
159 (define (install-complex-polar)
160 (define (make-from-real-imag-polar x y)
161 (cons (sqrt (+ (square x) (square y)))
163 (define (make-from-mag-ang-polar r a)
165 (define (real-part z) (* (magnitude z) (cos (angle z))))
166 (define (imag-part z) (* (magnitude z) (sin (angle z))))
167 (define (magnitude z) (car z))
168 (define (angle z) (cdr z))
169 (define (tag x) (attach-tag 'polar x))
170 (put 'real-part '(polar) real-part)
171 (put 'imag-part '(polar) imag-part)
172 (put 'magnitude '(polar) magnitude)
173 (put 'angle '(polar) angle)
174 (put 'make-from-real-imag 'polar
175 (lambda (x y) (tag (make-from-real-imag-polar x y))))
176 (put 'make-from-mag-ang 'polar
177 (lambda (r a) (tag (make-from-mag-ang-polar r a))))
179 (install-complex-rectangular)
180 (install-complex-polar)
181 ;; end rectangular and polar representations
183 (define (add-complex z1 z2)
184 (make-from-real-imag (+ (real-part z1) (real-part z2))
185 (+ (imag-part z1) (imag-part z2))))
186 (define (sub-complex z1 z2)
187 (make-from-real-imag (- (real-part z1) (real-part z2))
188 (- (imag-part z1) (imag-part z2))))
189 (define (mul-complex z1 z2)
190 (make-from-mag-ang (* (magnitude z1) (magnitude z2))
191 (+ (angle z1) (angle z2))))
192 (define (div-complex z1 z2)
193 (make-from-mag-ang (/ (magnitude z1) (magnitude z2))
194 (- (angle z1) (angle z2))))
195 (define (equ-complex? z1 z2)
196 (or (and (= (real-part z1) (real-part z2))
197 (= (imag-part z1) (imag-part z2))) ;; in case of rounding error
198 (and (= (magnitude z1) (magnitude z2))
199 (= (angle z1) (angle z2)))))
200 (define (=zero-complex? z)
201 (and (= (real-part z) 0)
202 (= (imag-part z) 0)))
204 (define (tag x) (attach-tag 'complex x))
205 (put 'add '(complex complex)
206 (lambda (z1 z2) (tag (add-complex z1 z2))))
207 (put 'sub '(complex complex)
208 (lambda (z1 z2) (tag (sub-complex z1 z2))))
209 (put 'mul '(complex complex)
210 (lambda (z1 z2) (tag (mul-complex z1 z2))))
211 (put 'div '(complex complex)
212 (lambda (z1 z2) (tag (div-complex z1 z2))))
213 (put 'equ? '(complex complex) equ-complex?)
214 (put '=zero? '(complex) =zero-complex?)
215 (put 'make-from-real-imag 'complex
216 (lambda (x y) (tag (make-from-real-imag x y))))
217 (put 'make-from-mag-ang 'complex
218 (lambda (r a) (tag (make-from-mag-ang r a))))
221 (define (make-scheme-number n)
222 ((get 'make 'scheme-number) n))
223 (define (make-rational n d)
224 ((get 'make 'rational) n d))
225 (define (make-complex-from-real-imag x y)
226 ((get 'make-from-real-imag 'complex) x y))
227 (define (make-complex-from-mag-ang r a)
228 ((get 'make-from-mag-ang 'complex) r a))
231 ;; install number packages
233 (install-scheme-number-package)
234 (install-rational-package)
235 (install-complex-package)
238 (define (test-case actual expected)
243 (display "Expected: ")
247 (test-case (equ? (div (make-scheme-number 81)
248 (mul (make-scheme-number 2)
249 (make-scheme-number 4.5)))
250 (add (make-scheme-number 4)
251 (make-scheme-number 5)))
253 (test-case (equ? (div (make-rational 4 2)
255 (sub (make-rational 9 1)
256 (mul (make-rational 4 1)
257 (make-rational 3 4))))
259 (test-case (equ? (add (make-complex-from-real-imag 3 4)
260 (make-complex-from-real-imag -5 -3))
261 '(complex rectangular -2 . 1))
263 (test-case (equ? (div (make-scheme-number 80)
264 (mul (make-scheme-number 2)
265 (make-scheme-number 4.5)))
266 (add (make-scheme-number 4)
267 (make-scheme-number 5)))
269 (test-case (equ? (div (make-rational 4 3)
271 (sub (make-rational 9 1)
272 (mul (make-rational 4 1)
273 (make-rational 3 4))))
275 (test-case (equ? (add (make-complex-from-real-imag 3 4.5)
276 (make-complex-from-real-imag -5 -3))
277 '(complex rectangular -2 . 1))
279 (test-case (=zero? (sub (div (make-scheme-number 81)
280 (mul (make-scheme-number 2)
281 (make-scheme-number 4.5)))
282 (add (make-scheme-number 4)
283 (make-scheme-number 5))))
285 (test-case (=zero? (sub (div (make-rational 4 2)
287 (sub (make-rational 9 1)
288 (mul (make-rational 4 1)
289 (make-rational 3 4)))))
291 (test-case (=zero? (sub (add (make-complex-from-real-imag 3 4)
292 (make-complex-from-real-imag -5 -3))
293 '(complex rectangular -2 . 1)))
295 (test-case (=zero? (sub (div (make-scheme-number 81)
296 (mul (make-scheme-number 2)
297 (make-scheme-number 4.5)))
298 (add (make-scheme-number 3.5)
299 (make-scheme-number 5))))
301 (test-case (=zero? (sub (div (make-rational 4 3)
303 (sub (make-rational 9 1)
304 (mul (make-rational 4 1)
305 (make-rational 3 4)))))
307 (test-case (=zero? (sub (add (make-complex-from-real-imag 3 5)
308 (make-complex-from-real-imag -5 -3))
309 '(complex rectangular -2 . 1)))
312 (define (scheme-number->complex n)
313 (make-complex-from-real-imag (contents n) 0))
314 (put-coercion 'scheme-number 'complex scheme-number->complex)
316 (define (apply-generic op . args)
317 (let ((type-tags (map type-tag args)))
318 (let ((proc (get op type-tags)))
320 (apply proc (map contents args))
321 (if (= (length args) 2)
322 (let ((type1 (car type-tags))
323 (type2 (cadr type-tags))
326 (let ((t1->t2 (get-coercion type1 type2))
327 (t2->t1 (get-coercion type2 type1)))
329 (apply-generic op (t1->t2 a1) a2))
331 (apply-generic op a1 (t2->t1 a2)))
334 (error "No method for these types"
335 (list op type-tags))))))
336 (error "No method for these types"
337 (list op type-tags)))))))
340 (test-case (add (make-scheme-number 5)
341 (make-complex-from-real-imag 3 2))
342 '(complex rectangular 8 . 2))
343 (test-case (add (make-complex-from-mag-ang 5 0.927295218)
344 (make-scheme-number 2))
345 '(complex rectangular 5 . 4))
347 ;; Exercise 2.82. Show how to generalize apply-generic to handle coercion in the general case of multiple arguments. One strategy is to attempt to coerce all the arguments to the type of the first argument, then to the type of the second argument, and so on. Give an example of a situation where this strategy (and likewise the two-argument version given above) is not sufficiently general. (Hint: Consider the case where there are some suitable mixed-type operations present in the table that will not be tried.)
349 (define (apply-generic op . args)
350 (let* ((type-tags (map type-tag args))
351 (proc (get op type-tags)))
353 (apply proc (map contents args))
354 (let* ((all-coercions ;; 2d-list of coercion procedures
355 (map (lambda (to-tag)
356 (map (lambda (from-tag)
357 (if (equal? from-tag to-tag)
359 (get-coercion from-tag to-tag)))
363 (filter (lambda (coercions)
364 (fold-left and #t coercions))
366 ;; #t if same type or if coercion procedure exists
367 (if (null? valid-coercions)
368 (error "No method for these types"
372 (map (lambda (coerce arg)
373 (if (equal? coerce 'same-tag)
376 (car valid-coercions)
378 ;; use the first to-type that all arguments can be coerced to
380 ;; It might be the case that a mixed-type operation would work where trying to force everything to the same type might not. For example, a contrived operation might work for data types '(float complex int) if we coerce float to complex to get '(complex complex int) but may not work with '(complex complex complex) or '(int int int).
382 ;; or maybe a supertype might work. Say we want to add '(imaginary real), then we might want to promote both to complex then add