#lang racket
;;; op table
(define op-table (make-hash))
(define (put op type item)
(hash-set! op-table (list op type) item))
(define (get op type)
(hash-ref op-table (list op type)))
;; I'm going to use lists instead of dotted pairs.
;;; generic operations
(define (attach-tag type-tag contents)
(list type-tag contents))
(define (type-tag datum)
(if (list? datum)
(first datum)
(error "bad tagged datum -- TYPE-TAG" datum)))
(define (contents datum)
(if (list? datum)
(second datum)
(error "bad tagged datum -- CONTENTS" datum)))
(define (apply-generic op . args)
(define type-tags (map type-tag args))
(define proc (get op type-tags))
(if proc
(apply proc (map contents args))
(error "no method for these types -- APPLY-GENERIC"
(list op type-tags))))
(define (add x y) (apply-generic 'add x y))
(define (sub x y) (apply-generic 'sub x y))
(define (mul x y) (apply-generic 'mul x y))
(define (div x y) (apply-generic 'div x y))
(define (equ? x y) (apply-generic 'equ x y))
(define (=zero? x) (apply-generic '=zero x))
(define (re-part z) (apply-generic 're-part z))
(define (im-part z) (apply-generic 'im-part z))
(define (mag-part z) (apply-generic 'mag-part z))
(define (ang-part z) (apply-generic 'ang-part z))
;;; constructors
(define (make-scheme-number n)
((get 'make 'scheme-number) n))
(define (make-rational n d)
((get 'make 'rational) n d))
(define (make-complex-from-real-imag x y)
((get 'make-from-real-imag 'complex) x y))
(define (make-complex-from-mag-ang r a)
((get 'make-from-mag-ang 'complex) r a))
;;; scheme numbers
(define (install-scheme-number-package)
(define (tag x)
(attach-tag 'scheme-number x))
(put 'add '(scheme-number scheme-number)
(lambda (x y) (tag (+ x y))))
(put 'sub '(scheme-number scheme-number)
(lambda (x y) (tag (- x y))))
(put 'mul '(scheme-number scheme-number)
(lambda (x y) (tag (* x y))))
(put 'div '(scheme-number scheme-number)
(lambda (x y) (tag (/ x y))))
(put 'make 'scheme-number
(lambda (x) (tag x)))
(put 'equ '(scheme-number scheme-number)
=)
(put '=zero '(scheme-number)
zero?)
'done)
(install-scheme-number-package)
;;; rational numbers
(define (install-rational-package)
;; internal procedures
(define (numer x) (first x))
(define (denom x) (second x))
(define (make-rat n d)
(let ((g (gcd n d)))
(list (/ n g) (/ d g))))
(define (add-rat x y)
(make-rat (+ (* (numer x) (denom y))
(* (numer y) (denom x)))
(* (denom x) (denom y))))
(define (sub-rat x y)
(make-rat (- (* (numer x) (denom y))
(* (numer y) (denom x)))
(* (denom x) (denom y))))
(define (mul-rat x y)
(make-rat (* (numer x) (numer y))
(* (denom x) (denom y))))
(define (div-rat x y)
(make-rat (* (numer x) (denom y))
(* (denom x) (numer y))))
(define (equ? x y)
(= (* (numer x) (denom y))
(* (denom x) (numer y))))
(define (=zero? x) (= (numer x) 0))
;; interface to the rest of the system
(define (tag x) (attach-tag 'rational x))
(put 'equ '(rational rational)
(lambda (x y) (equ? x y)))
(put '=zero '(rational)
(lambda (x) (=zero? x)))
(put 'add '(rational rational)
(lambda (x y) (tag (add-rat x y))))
(put 'sub '(rational rational)
(lambda (x y) (tag (sub-rat x y))))
(put 'mul '(rational rational)
(lambda (x y) (tag (mul-rat x y))))
(put 'div '(rational rational)
(lambda (x y) (tag (div-rat x y))))
(put 'make 'rational
(lambda (n d) (tag (make-rat n d))))
'done)
(install-rational-package)
;;; complex rectangular numbers
(define (install-rectangular-package)
;; internal procedures
(define (re-part z) (first z))
(define (im-part z) (second z))
(define (make-from-real-imag x y) (list x y))
(define (mag-part z)
(sqrt (+ (sqr (re-part z))
(sqr (im-part z)))))
(define (ang-part z)
(atan (im-part z) (re-part z)))
(define (make-from-mag-ang r a)
(list (* r (cos a)) (* r (sin a))))
;; interface to the rest of the system
(define (tag x) (attach-tag 'rectangular x))
(put 're-part '(rectangular) re-part)
(put 'im-part '(rectangular) im-part)
(put 'mag-part '(rectangular) mag-part)
(put 'ang-part '(rectangular) ang-part)
(put 'make-from-real-imag 'rectangular
(lambda (x y) (tag (make-from-real-imag x y))))
(put 'make-from-mag-ang 'rectangular
(lambda (r a) (tag (make-from-mag-ang r a))))
'done)
(install-rectangular-package)
;;; complex polar numbers
(define (install-polar-package)
;; internal procedures
(define (mag-part z) (first z))
(define (ang-part z) (second z))
(define (make-from-mag-ang r a) (list r a))
(define (re-part z)
(* (mag-part z) (cos (ang-part z))))
(define (im-part z)
(* (mag-part z) (sin (ang-part z))))
(define (make-from-real-imag x y)
(list (sqrt (+ (sqr x) (sqr y)))
(atan y x)))
;; interface to the rest of the system
(define (tag x) (attach-tag 'polar x))
(put 're-part '(polar) re-part)
(put 'im-part '(polar) im-part)
(put 'mag-part '(polar) mag-part)
(put 'ang-part '(polar) ang-part)
(put 'make-from-real-imag 'polar
(lambda (x y) (tag (make-from-real-imag x y))))
(put 'make-from-mag-ang 'polar
(lambda (r a) (tag (make-from-mag-ang r a))))
'done)
(install-polar-package)
;;; generic complex numbers
(define (install-complex-package)
;; imported procedures from rectangular and polar packages
(define (make-from-real-imag x y)
((get 'make-from-real-imag 'rectangular) x y))
(define (make-from-mag-ang r a)
((get 'make-from-mag-ang 'polar) r a))
;; internal procedures
(define (add-complex z1 z2)
(make-from-real-imag (+ (re-part z1) (re-part z2))
(+ (im-part z1) (im-part z2))))
(define (sub-complex z1 z2)
(make-from-real-imag (- (re-part z1) (re-part z2))
(- (im-part z1) (im-part z2))))
(define (mul-complex z1 z2)
(make-from-mag-ang (* (mag-part z1) (mag-part z2))
(+ (ang-part z1) (ang-part z2))))
(define (div-complex z1 z2)
(make-from-mag-ang (/ (mag-part z1) (mag-part z2))
(- (ang-part z1) (ang-part z2))))
(define (equ? z1 z2)
; account for round-off error
(and
(< (abs (- (re-part z1) (re-part z2))) 0.0001)
(< (abs (- (im-part z1) (im-part z2))) 0.0001)))
(define (=zero? z)
(or (= (mag-part z) 0)
; account for round-off error
(and (< (abs (re-part z)) 0.0001)
(< (abs (im-part z)) 0.0001))))
;; interface to the rest of the system
(put 're-part '(complex) re-part)
(put 'im-part '(complex) im-part)
(put 'mag-part '(complex) mag-part)
(put 'ang-part '(complex) ang-part)
(define (tag z) (attach-tag 'complex z))
(put 'equ '(complex complex)
(lambda (z1 z2) (equ? z1 z2)))
(put '=zero '(complex)
(lambda (z) (=zero? z)))
(put 'add '(complex complex)
(lambda (z1 z2) (tag (add-complex z1 z2))))
(put 'sub '(complex complex)
(lambda (z1 z2) (tag (sub-complex z1 z2))))
(put 'mul '(complex complex)
(lambda (z1 z2) (tag (mul-complex z1 z2))))
(put 'div '(complex complex)
(lambda (z1 z2) (tag (div-complex z1 z2))))
(put 'make-from-real-imag 'complex
(lambda (x y) (tag (make-from-real-imag x y))))
(put 'make-from-mag-ang 'complex
(lambda (r a) (tag (make-from-mag-ang r a))))
'done)
(install-complex-package)
(define q1 (make-rational 1 2))
(define q2 (make-rational 3 4))
(add q1 q2)
;; '(rational (5 4))
(sub q1 q2)
;; '(rational (-1 4))
(mul q1 q2)
;; '(rational (3 8))
(div q1 q2)
;; '(rational (2 3))
(define z1 (make-complex-from-real-imag 3 4))
(define z2 (make-complex-from-mag-ang 1 pi))
(add z1 z2)
;; '(complex (rectangular (2.0 4.0)))
(sub z1 z2)
;; '(complex (rectangular (4.0 4.0)))
(mul z1 z2)
;; '(complex (polar (5 4.068887871591405)))
(div z1 z2)
;; '(complex (polar (5 -2.214297435588181)))
;;;;;;;;;;
;; 2.77 ;;
;;;;;;;;;;
;; I'm using mag-part for magnitude so as not to conflict with the built-in racket
;; magnitude function. To be consistent I'll also use ang-part instead of angle.
(define z '(complex (rectangular (3 4))))
;; without the added lines
;; (mag-part z)
;; ;; hash-ref: no value found for key
;; ;; key: '(mag-part (complex))
;; with the added lines
(mag-part z)
;; 5
;; In evaluating (mag-part z), apply-generic is called twice. The first call
;; strips off the 'complex tag and dispatches to the generic mag-part again. This
;; is because of the lines we added to the complex package. The second call
;; strips off the 'rectangular tag and dispatches the mag-part procedure defined
;; in the rectangular package.
;; the manual trace
;; recall that (apply-generic op . args)
;; is (apply proc (map contents args))
;; where proc is (get op (map type-tag args))
;; (mag-part z)
;; (mag-part '(complex (rectangular (3 4))))
;; (apply-generic 'mag-part '(complex (rectangular (3 4))))
;; ;; this first call to apply-generic strips off the 'complex tag
;; (apply (get 'mag-part 'complex) '(rectangular (3 4)))
;; ;; the lines we added to the complex package say to use
;; ;; the generic mag-part again for (get 'mag-part 'complex)
;; (mag-part '(rectangular (3 4)))
;; (apply-generic 'mag-part '(rectangular (3 4)))
;; ;; this second call to apply-generic strips off the 'rectangular tag
;; ;; and uses the mag-part function defined in the complex rectangular package
;; (apply (get 'mag-part 'rectangular) '(3 4))
;; (apply (lambda (z) (sqrt (+ (sqr (re-part z)) (sqr (im-part z))))) '(3 4))
;; (sqrt (+ (sqr 3) (sqr 4)))
;; 5
;;;;;;;;;;
;; 2.78 ;;
;;;;;;;;;;
;; (define (attach-tag type-tag contents)
;; (if (number? contents)
;; contents
;; (list type-tag contents)))
;; (define (type-tag datum)
;; (cond [(number? datum) 'scheme-number]
;; [(list? datum) (first datum)]
;; [else (error "bad tagged datum -- TYPE-TAG" datum)]))
;; (define (contents datum)
;; (cond [(number? datum) datum]
;; [(list? datum) (second datum)]
;; [else (error "bad tagged datum -- CONTENTS" datum)]))
;;;;;;;;;;
;; 2.79 ;;
;;;;;;;;;;
;; Here is the code that was added above to implement equ?:
;; (define (equ? x y) (apply-generic 'equ x y))
;; ;; inside the scheme number package
;; (put 'equ '(scheme-number scheme-number)
;; =)
;; ;; inside the rational number package
;; (define (equ? x y)
;; (= (* (numer x) (denom y))
;; (* (denom x) (numer y))))
;; (put 'equ '(rational rational)
;; (lambda (x y) (equ? x y)))
;; ;; inside the generic complex number package
;; (define (equ? z1 z2)
;; ; account for round-off error
;; (and
;; (< (abs (- (re-part z1) (re-part z2))) 0.0001)
;; (< (abs (- (im-part z1) (im-part z2))) 0.0001)))
;; (put 'equ '(complex complex)
;; (lambda (z1 z2) (equ? z1 z2)))
(equ? (make-scheme-number 1)
(make-scheme-number 1))
;; #t
(equ? (make-scheme-number 2)
(make-scheme-number 3))
;; #f
(equ? (make-rational 1 2) (make-rational 3 6))
;; #t
(equ? (make-rational 1 2) (make-rational 2 3))
;; #f
(equ? (make-complex-from-real-imag -1 0)
(make-complex-from-mag-ang 1 pi))
;; #t
(equ? (make-complex-from-real-imag 3 4)
(make-complex-from-mag-ang 3 4))
;; #f
;;;;;;;;;;
;; 2.80 ;;
;;;;;;;;;;
;; Here is the code that was added above to implement =zero?:
;; (define (=zero? x) (apply-generic '=zero x))
;; ;; inside the scheme number package
;; (put '=zero '(scheme-number)
;; zero?)
;; ;; inside the rational number package
;; (define (=zero? x) (= (numer x) 0))
;; (put '=zero '(rational)
;; (lambda (x) (=zero? x)))
;; ;; inside the generic complex numbers package
;; (define (=zero? z)
;; (or (= (mag-part z) 0)
;; ; account for round-off error
;; (and (< (abs (re-part z)) 0.0001)
;; (< (abs (im-part z)) 0.0001))))
;; (put '=zero '(complex)
;; (lambda (z) (=zero? z)))
(=zero? (make-scheme-number 0))
;; #t
(=zero? (make-scheme-number 2))
;; #f
(=zero? (sub (make-rational 1 2)
(make-rational 5 10)))
;; #t
(=zero? (sub (make-complex-from-real-imag 1 0)
(make-complex-from-mag-ang 1 pi)))
;; #f
(=zero? (sub (make-complex-from-real-imag -1 0)
(make-complex-from-mag-ang 1 pi)))
;; #t
Comments
0 B
|👍
/👎