#lang racket ;;;;;;;;;;; ;; NOTES ;; ;;;;;;;;;;; ;; The generic arithmetic program through exercise 2.86, using type coercion, type ;; simplification, and generic components for complex numbers. ;; Note that integers are represented as un-tagged Racket exact integers like 1, ;; and reals are represented as un-tagged Racket inexact numbers like 1.0. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; OP, RAISE, AND PROJECT TABLES ;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (define op-table (make-hash)) (define (put op tag-list procedure) (hash-set! op-table (list op tag-list) procedure)) (define (can-apply? op tag-list) (member (list op tag-list) (hash-keys op-table))) (define (get op tag-list) (hash-ref op-table (list op tag-list))) (define project-table (make-hash)) (define (put-project type procedure) (hash-set! project-table type procedure)) (define (can-project? arg) (member (type-tag arg) (hash-keys project-table))) (define (project arg) ((hash-ref project-table (type-tag arg)) arg)) (define raise-table (make-hash)) (define (put-raise type procedure) (hash-set! raise-table type procedure)) (define (can-raise? arg) (member (type-tag arg) (hash-keys raise-table))) (define (raise arg) ((hash-ref raise-table (type-tag arg)) arg)) ;;;;;;;;;;;;;;;;;;;;;;;; ;; GENERIC OPERATIONS ;; ;;;;;;;;;;;;;;;;;;;;;;;; (define (attach-tag type-tag contents) (cond [(exact-integer? contents) contents] [(inexact-real? contents) contents] [else (list type-tag contents)])) (define (type-tag datum) (cond [(exact-integer? datum) 'integer] [(inexact-real? datum) 'real] [(pair? datum) (first datum)] [else (error "bad tagged-datum -- TYPE-TAG" datum)])) (define (contents datum) (cond [(exact-integer? datum) datum] [(inexact-real? datum) datum] [(pair? datum) (second datum)] [else (error "bad tagged datum -- CONTENTS" datum)])) (define (simplify arg) (cond [(can-project? arg) (define projected-arg (project arg)) (if (equ? arg (raise projected-arg)) (simplify projected-arg) arg)] [else arg])) (define (highest-type tag-list) ;; Raise 0 through all the types. Keep the highest type found in tag-list. (define (loop type arg) (cond [(can-raise? arg) (define new-arg (raise arg)) (define new-type (type-tag new-arg)) (if (member new-type tag-list) (loop new-type new-arg) (loop type new-arg))] [else type])) (loop 'integer 0)) (define (coerce args target-type) (define (coerce-one arg) (if (eq? (type-tag arg) target-type) arg (coerce-one (raise arg)))) (map coerce-one args)) (define (all-same? symbols) (or (< (length symbols) 2) (andmap (lambda (s) (eq? s (first symbols))) (rest symbols)))) (define (apply-generic op . args) (define type-tags (map type-tag args)) (cond [(can-apply? op type-tags) (apply (get op type-tags) (map contents args))] [(not (all-same? type-tags)) (define new-args (coerce args (highest-type type-tags))) (apply apply-generic (cons op new-args))] [else (error "no method for this op -- APPLY-GENERIC" op)])) ;; Only simplify the numerical operations and the complex selectors. ;; Do not simplify predicates or constructors. ;; arithmetic (define (add x y) (simplify (apply-generic 'add x y))) (define (sub x y) (simplify (apply-generic 'sub x y))) (define (mul x y) (simplify (apply-generic 'mul x y))) (define (div x y) (simplify (apply-generic 'div x y))) ;; predicates (define (=zero? x) (apply-generic '=zero? x)) (define (equ? x y) (apply-generic 'equ? x y)) ;; complex number selectors (define (re-part z) (simplify (apply-generic 're-part z))) (define (im-part z) (simplify (apply-generic 'im-part z))) (define (mag-part z) (simplify (apply-generic 'mag-part z))) (define (ang-part z) (simplify (apply-generic 'ang-part z))) ;; numerical operations needed for generic complex number components (define (absolute x) (apply-generic 'absolute x)) (define (expo x y) (simplify (apply-generic 'expo x y))) (define (arctan y x) (simplify (apply-generic 'arctan y x))) (define (cosine x) (simplify (apply-generic 'cosine x))) (define (sine x) (simplify (apply-generic 'sine x))) (define (square x) (simplify (apply-generic 'square x))) (define (square-root x) (simplify (apply-generic 'square-root x))) ;; constructors (define (make-integer a) ((get 'make 'integer) a)) (define (make-rational n d) ((get 'make 'rational) n d)) (define (make-real x) ((get 'make 'real) x)) (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)) ;; Note that we cannot simply define make-integer as (get 'make 'integer) ;; because that procedure has not been stored in the op-table yet. ;;;;;;;;;;;;;; ;; INTEGERS ;; ;;;;;;;;;;;;;; (define (install-integer-package) ;; internal procedures (define (make-integer a) (define flr (floor a)) (if (exact? flr) flr (inexact->exact flr))) (define (arctan-int b a) (make-real (atan b a))) (define (cosine-int a) (make-real (cos a))) (define (sine-int a) (make-real (sin a))) (define (square-root-int a) (make-real (sqrt a))) (define (raise-int datum) (define a (contents datum)) (make-rational a 1)) ;; interface to the rest of the system (put '=zero? '(integer) zero?) (put 'absolute '(integer) abs) (put 'add '(integer integer) +) (put 'arctan '(integer integer) arctan-int) (put 'cosine '(integer) cosine-int) (put 'div '(integer integer) make-rational) (put 'equ? '(integer integer) =) (put 'expo '(integer integer) expt) (put 'make 'integer make-integer) (put 'mul '(integer integer) *) (put 'sine '(integer) sine-int) (put 'square-root '(integer) square-root-int) (put 'square '(integer) sqr) (put 'sub '(integer integer) -) (put-raise 'integer raise-int) 'done-installing-integers) (install-integer-package) ;;;;;;;;;;;;;;;;;;;;;; ;; RATIONAL NUMBERS ;; ;;;;;;;;;;;;;;;;;;;;;; (define (install-rational-package) ;; internal procedures (define (make-rat n d) (let ([g (gcd n d)]) (list (make-integer (/ n g)) (make-integer (/ d g))))) (define (numer x) (first x)) (define (denom x) (second x)) (define (=zero-rat? x) (zero? (numer x))) (define (equ-rat? x y) (= (* (numer x) (denom y)) (* (denom x) (numer y)))) (define (absolute-rat x) (make-real (abs (/ (numer x) (denom x))))) (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 (expo-rat x y) (make-real (expt (/ (* (numer x) 1.0) (denom x)) (/ (* (numer y) 1.0) (denom y))))) (define (arctan-rat y x) (make-real (atan (/ (numer y) (denom y)) (/ (numer x) (denom x))))) (define (cosine-rat x) (make-real (cos (/ (numer x) (denom x))))) (define (sine-rat x) (make-real (sin (/ (numer x) (denom x))))) (define (square-root-rat x) (make-real (sqrt (/ (numer x) (denom x))))) (define (square-rat x) (make-rat (sqr (numer x)) (sqr (denom x)))) (define (project-rat datum) (define x (contents datum)) (make-integer (floor (/ (numer x) (denom x))))) (define (raise-rat datum) (define x (contents datum)) (make-real (/ (numer x) (denom x)))) ;; interface to the rest of the system (define (tag x) (attach-tag 'rational x)) (put 'make 'rational (compose tag make-rat)) (put '=zero? '(rational) =zero-rat?) (put 'equ? '(rational rational) equ-rat?) (put 'absolute '(rational) absolute-rat) (put 'add '(rational rational) (compose tag add-rat)) (put 'sub '(rational rational) (compose tag sub-rat)) (put 'mul '(rational rational) (compose tag mul-rat)) (put 'div '(rational rational) (compose tag div-rat)) (put 'expo '(rational rational) expo-rat) (put 'arctan '(rational rational) arctan-rat) (put 'cosine '(rational) cosine-rat) (put 'sine '(rational) sine-rat) (put 'square-root '(rational) square-root-rat) (put 'square '(rational) (compose tag square-rat)) (put-project 'rational project-rat) (put-raise 'rational raise-rat) 'done-installing-rationals) (install-rational-package) ;;;;;;;;;;;;;;;;;; ;; REAL NUMBERS ;; ;;;;;;;;;;;;;;;;;; (define (install-real-package) ;; internal procedures (define (make-real x) (if (inexact? x) x (exact->inexact x))) (define (project-real datum) (define x (contents datum)) (make-rational (floor (* x 1000000)) 1000000)) (define (raise-real datum) (define x (contents datum)) (make-complex-from-real-imag x 0)) ;; interface to the rest of the system (put '=zero? '(real) zero?) (put 'absolute '(real) abs) (put 'add '(real real) +) (put 'arctan '(real real) atan) (put 'cosine '(real) cos) (put 'div '(real real) /) (put 'equ? '(real real) =) (put 'expo '(real real) expt) (put 'make 'real make-real) (put 'mul '(real real) *) (put 'sine '(real) sin) (put 'square-root '(real) sqrt) (put 'square '(real) sqr) (put 'sub '(real real) -) (put-project 'real project-real) (put-raise 'real raise-real) 'done-installing-reals) (install-real-package) ;;;;;;;;;;;;;;;;;;;;; ;; COMPLEX NUMBERS ;; ;;;;;;;;;;;;;;;;;;;;; (define (install-complex-package) ;;; rectangular complex numbers (define (install-rectangular-package) ;; internal procedures (define (make-from-real-imag-rect x y) (list x y)) (define (re-part-rect z) (first z)) (define (im-part-rect z) (second z)) (define (make-from-mag-ang-rect r a) (list (mul r (cosine a)) (mul r (sine a)))) (define (mag-part-rect z) (square-root (add (square (re-part-rect z)) (square (im-part-rect z))))) (define (ang-part-rect z) (arctan (im-part-rect z) (re-part-rect z))) ;; interface to the rest of the system (define (tag z) (attach-tag 'rectangular z)) (put 're-part '(rectangular) re-part-rect) (put 'im-part '(rectangular) im-part-rect) (put 'mag-part '(rectangular) mag-part-rect) (put 'ang-part '(rectangular) ang-part-rect) (put 'make-from-real-imag 'rectangular (compose tag make-from-real-imag-rect)) (put 'make-from-mag-ang 'rectangular (compose tag make-from-mag-ang-rect)) 'done) (install-rectangular-package) ;;; polar complex numbers (define (install-polar-package) ;; internal procedures (define (make-from-mag-ang-polar r a) (list r a)) (define (mag-part-polar z) (first z)) (define (ang-part-polar z) (second z)) (define (make-from-real-imag-polar x y) (list (square-root (add (square x) (square y))) (arctan y x))) (define (re-part-polar z) (mul (mag-part-polar z) (cosine (ang-part-polar z)))) (define (im-part-polar z) (mul (mag-part-polar z) (sine (ang-part-polar z)))) ;; interface to the rest of the system (define (tag x) (attach-tag 'polar x)) (put 're-part '(polar) re-part-polar) (put 'im-part '(polar) im-part-polar) (put 'mag-part '(polar) mag-part-polar) (put 'ang-part '(polar) ang-part-polar) (put 'make-from-real-imag 'polar (compose tag make-from-real-imag-polar)) (put 'make-from-mag-ang 'polar (compose tag make-from-mag-ang-polar)) 'done) (install-polar-package) ;;; generic complex numbers ;; internal procedures ; constructors (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)) ; predicates (define (equ-complex? z1 z2) (and (< (absolute (sub (re-part z1) (re-part z2))) 0.000001) (< (absolute (sub (im-part z1) (im-part z2))) 0.000001))) (define (=zero-complex? z) (or (=zero? (mag-part z)) (and (< (absolute (re-part z)) 0.000001) (< (absolute (im-part z)) 0.000001)))) ; arithmetic (define (add-complex z1 z2) (make-from-real-imag (add (re-part z1) (re-part z2)) (add (im-part z1) (im-part z2)))) (define (sub-complex z1 z2) (make-from-real-imag (sub (re-part z1) (re-part z2)) (sub (im-part z1) (im-part z2)))) (define (mul-complex z1 z2) (make-from-mag-ang (mul (mag-part z1) (mag-part z2)) (add (ang-part z1) (ang-part z2)))) (define (div-complex z1 z2) (make-from-mag-ang (div (mag-part z1) (mag-part z2)) (sub (ang-part z1) (ang-part z2)))) (define (expo-complex z1 z2) (define w (expt (+ (make-real (re-part z1)) (* (make-real (im-part z1)) 0+i)) (+ (make-real (re-part z2)) (* (make-real (im-part z2)) 0+i)))) (make-from-real-imag (real-part w) (imag-part w))) (define (project-complex datum) (define z (contents datum)) (make-real (re-part z))) ;; interface to the rest of the system (define (tag z) (attach-tag 'complex z)) (put 'make-from-real-imag 'complex (compose tag make-from-real-imag)) (put 'make-from-mag-ang 'complex (compose tag make-from-mag-ang)) (put '=zero? '(complex) =zero-complex?) (put 'equ? '(complex complex) equ-complex?) (put 're-part '(complex) re-part) (put 'im-part '(complex) im-part) (put 'mag-part '(complex) mag-part) (put 'ang-part '(complex) ang-part) (put 'add '(complex complex) (compose tag add-complex)) (put 'sub '(complex complex) (compose tag sub-complex)) (put 'mul '(complex complex) (compose tag mul-complex)) (put 'div '(complex complex) (compose tag div-complex)) (put 'expo '(complex complex) (compose tag expo-complex)) (put-project 'complex project-complex) 'done-installing-complex-numbers) (install-complex-package) ;;;;;;;;;;; ;; TESTS ;; ;;;;;;;;;;; (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 4))) (sub z1 z2) ;; '(complex (rectangular (4 4))) (mul z1 z2) ;; '(complex (polar (5 4.068887871591405))) (div z1 z2) ;; '(complex (polar (5 -2.214297435588181))) (define w (make-complex-from-real-imag 0.5 (make-rational 3 4))) (re-part w) ;; '(rational (1 2)) (im-part w) ;; '(rational (3 4)) (=zero? (sub w (make-complex-from-real-imag (make-rational 1 2) 0.75))) ;; #t