sicp/generics.rkt

1054 lines
34 KiB
Racket

#lang sicp
(#%require racket/trace)
;; This file covers exercises 2.78 to 2.97.
;;;from section 1.2.5, for Section 2.1.1
(define (gcd a b)
(if (= b 0)
a
(gcd b (remainder a b))))
;;;-----------
;;;from section 3.3.3 for section 2.4.3
;;; to support operation/type table for data-directed dispatch
(define (assoc key records)
(cond ((null? records) false)
((equal? key (caar records)) (car records))
(else (assoc key (cdr records)))))
(define (make-table)
(let ((local-table (list '*table*)))
(define (lookup key-1 key-2)
(let ((subtable (assoc key-1 (cdr local-table))))
(if subtable
(let ((record (assoc key-2 (cdr subtable))))
(if record
(cdr record)
false))
false)))
(define (insert! key-1 key-2 value)
(let ((subtable (assoc key-1 (cdr local-table))))
(if subtable
(let ((record (assoc key-2 (cdr subtable))))
(if record
(set-cdr! record value)
(set-cdr! subtable
(cons (cons key-2 value)
(cdr subtable)))))
(set-cdr! local-table
(cons (list key-1
(cons key-2 value))
(cdr local-table)))))
'ok)
(define (dispatch m)
(cond ((eq? m 'lookup-proc) lookup)
((eq? m 'insert-proc!) insert!)
(else (error "Unknown operation -- TABLE" m))))
dispatch))
(define operation-table (make-table))
(define get (operation-table 'lookup-proc))
(define put (operation-table 'insert-proc!))
(define coercion-table (make-table))
(define get-coercion (coercion-table 'lookup-proc))
(define put-coercion (coercion-table 'insert-proc!))
;; Apply generic
;;(define (apply-generic op . args)
;; (let ((type-tags (map type-tag args)))
;; (let ((proc (get op type-tags)))
;; (if proc
;; (apply proc (map contents args))
;; (error
;; "No method for these types -- APPLY-GENERIC"
;; (list op type-tags))))))
;; Number tower. Ignores possibility of one of the arguments
;; not being in the tower. Maybe fix later.
(define (lower-in-tower? t1 t2)
(define tower '(integer rational scheme-number complex))
(define (tower-iter tower)
(cond ((null? tower)
(error "Not in tower" (list t1 t2)))
((eq? (car tower) t1) #t)
((eq? (car tower) t2) #f)
(else (tower-iter (cdr tower)))))
(if (eq? t1 t2)
#f
(tower-iter tower)))
(define tower '(integer rational scheme-number complex))
(define (transform-type x tower)
(define (next-type type tower)
(cond ((or (null? tower)
(null? (cdr tower))) '())
((eq? type (car tower)) (cadr tower))
(else (next-type type (cdr tower)))))
(and (type-tagged? x)
(let* ((type (type-tag x))
(proj-type (next-type type tower)))
(if (null? proj-type)
'()
((get-coercion type proj-type) (contents x))))))
(define (raise x)
(transform-type x tower))
(define (project x)
(transform-type x (reverse tower)))
(define (apply-generic op . args)
(define (coerce-to t)
(letrec ((coerce (lambda (arg)
(cond ((eq? (type-tag arg) t) arg)
((lower-in-tower?
(type-tag arg)
t)
(coerce (raise arg)))
(else '())))))
coerce))
(define (any-empty-list l)
(cond ((null? l) #f)
((eq? (car l) '()) #t)
(else (any-empty-list (cdr l)))))
(define (coerce-args args type-tags)
(if (null? type-tags)
'()
(let* ((type (car type-tags))
(coerced-args (map (coerce-to type) args)))
(if (not (any-empty-list coerced-args))
coerced-args
(coerce-args args (cdr type-tags))))))
(let* ((type-tags (map type-tag args))
(proc (get op type-tags))
(result
(if proc
(apply proc (map contents args))
(if (> (length args) 1)
(let ((coerced-args (coerce-args args type-tags)))
(if (not (null? coerced-args))
(apply apply-generic (cons op coerced-args))
(error "Can't coerce arguments to a common type"
(list op type-tags))))
(error "No matching operator for args" op args)))))
result))
;; scheme-number is for reals only
(define (scheme-number? x)
(and (real? x)
(not (integer? x))))
(define (attach-tag type-tag contents)
(cond ((eq? type-tag 'scheme-number) contents)
(else (cons type-tag contents))))
(define (type-tag datum)
(cond ((number? datum) 'scheme-number)
((pair? datum) (car datum))
(else (error "Bad tagged datum -- TYPE-TAG" datum))))
(define (contents datum)
(cond ((number? datum) datum)
((pair? datum) (cdr datum))
(else (error "Bad tagged datum -- CONTENTS" datum))))
(define type-tagged?
(lambda (x)
(or (number? x) (pair? x))))
;; Scheme numbers
(define (install-scheme-number-package)
(define (reduce-integers n d)
(let ((g (gcd n d)))
(list (/ n g) (/ d g))))
(define (tag x)
(attach-tag 'scheme-number x))
(define (exp x y) (apply-generic 'exp x y))
(put 'add '(scheme-number scheme-number)
(lambda (x y) (tag (+ x y))))
(put 'add3 '(scheme-number scheme-number scheme-number)
(lambda (x y z) (tag (+ x y z))))
(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 'negate '(scheme-number)
(lambda (x) (tag (* -1 x))))
(put 'sine '(scheme-number)
(lambda (x) (tag (sin x))))
(put 'cosine '(scheme-number)
(lambda (x) (tag (cos x))))
(put 'square-root '(scheme-number)
(lambda (x) (tag (sqrt x))))
(put 'greatest-common-divisor '(scheme-number scheme-number)
(lambda (a b) (tag (gcd a b))))
(put 'reduce '(scheme-number scheme-number)
(lambda (a b)
(let ((reduced (reduce-integers a b)))
(list (tag (car reduced))
(tag (cadr reduced))))))
(put 'equ? '(scheme-number scheme-number) =)
(put '=zero? '(scheme-number)
(lambda (x) (= x 0)))
(put-coercion 'scheme-number 'complex
(lambda (x)
(make-complex-from-real-imag
(tag x)
(tag 0))))
(put-coercion 'scheme-number 'rational
(lambda (x)
(make-rational
(round (* x 1000))
1000)))
(put 'exp '(scheme-number scheme-number)
(lambda (x y) (tag (expt x y))))
(put 'make 'scheme-number
(lambda (x) (tag x)))
'done)
;; Integers
(define (install-integer-package)
(define (tag x)
(attach-tag 'integer x))
(define (raise-int x)
(make-rational x 1))
(put 'add '(integer integer)
(lambda (x y)
(make-integer (+ x y))))
(put 'sub '(integer integer)
(lambda (x y)
(make-integer (- x y))))
(put 'div '(integer integer)
(lambda (x y)
(make-integer (/ x y))))
(put 'mul '(integer integer)
(lambda (x y)
(make-integer (* x y))))
(put 'negate '(integer)
(lambda (x) (* -1 x)))
(put 'sine '(integer)
(lambda (x) (make-scheme-number (sin x))))
(put 'cosine '(integer)
(lambda (x) (cos x)))
(put 'square-root '(integer)
(lambda (x) (make-scheme-number (sqrt x))))
(put 'arctan '(integer integer)
(lambda (x y) (make-scheme-number (atan x y))))
(put 'equ? '(integer integer)
(lambda (x y)
(= x y)))
(put-coercion 'integer 'rational raise-int)
(put 'make 'integer
(lambda (x) (tag (round x))))
'done)
;; Rationals
(define (install-rational-package)
;; internal procedures
(define (numer x) (car x))
(define (denom x) (cdr x))
(define (make-rat n d)
(let ((reduced (reduce n d)))
(cons
(car reduced)
(cadr reduced))))
(define (add-rat x y)
(make-rat (add (mul (numer x) (denom y))
(mul (numer y) (denom x)))
(mul (denom x) (denom y))))
(define (sub-rat x y)
(make-rat (sub (mul (numer x) (denom y))
(mul (numer y) (denom x)))
(mul (denom x) (denom y))))
(define (mul-rat x y)
(make-rat (mul (numer x) (numer y))
(mul (denom x) (denom y))))
(define (div-rat x y)
(make-rat (mul (numer x) (denom y))
(mul (denom x) (numer y))))
(define (eq-rat x y)
(and (= (numer x) (numer y))
(= (denom x) (denom y))))
(define (raise-rat x)
(make-scheme-number (/ (numer x) (denom x))))
;; interface to rest of the system
(define (tag x) (attach-tag 'rational x))
(put 'add '(rational rational)
(lambda (x y) (tag (add-rat x y))))
(put 'add3 '(rational rational rational)
(lambda (x y z) (tag (add-rat x (add-rat y z)))))
(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 'negate '(rational)
(lambda (x)
(make-rational (* -1 (numer x)) (denom x))))
(put 'sine '(rational)
(lambda (x) (sine (raise-rat x))))
(put 'cosine '(rational)
(lambda (x) (cosine (raise-rat x))))
(put 'square-root '(rational)
(lambda (x) (square-root (raise-rat x))))
(put 'arctan '(rational rational)
(lambda (x y) (arctan (raise-rat x)
(raise-rat y))))
(put 'equ? '(rational rational) eq-rat)
(put '=zero? '(rational)
(lambda (x) (equ? (numer x) (make-integer 0))))
(put-coercion 'rational 'scheme-number raise-rat)
(put-coercion 'rational 'integer
(lambda (x)
(make-integer
(/ (numer x) (denom x)))))
(put 'make 'rational
(lambda (n d) (tag (make-rat n d))))
'done)
;;;-----------
;;;SECTION 2.4.3
;; uses get/put (from 3.3.3) -- see ch2support.scm
(define (install-rectangular-package)
;; internal procedures
(define (real-part z) (car z))
(define (imag-part z) (cdr z))
(define (make-from-real-imag x y) (cons x y))
(define (magnitude z)
(square-root (add (square (real-part z))
(square (imag-part z)))))
(define (angle z)
(arctan (imag-part z) (real-part z)))
(define (make-from-mag-ang r a)
(cons (mul r (cosine a))
(mul r (sine a))))
(define (negate z)
(make-from-real-imag
(mul (make-scheme-number -1) (real-part z))
(mul (make-scheme-number -1) (imag-part z))))
;; interface to the rest of the system
(define (tag x) (attach-tag 'rectangular x))
(put 'real-part '(rectangular) real-part)
(put 'imag-part '(rectangular) imag-part)
(put 'magnitude '(rectangular) magnitude)
(put 'angle '(rectangular) angle)
(put 'negate '(rectangular)
(lambda (z) (tag (negate z))))
;;(put 'project '(rectangular)
;; (lambda (z) (real-part z)))
(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)
(define (install-polar-package)
;; internal procedures
(define (magnitude z) (car z))
(define (angle z) (cdr z))
(define (make-from-mag-ang r a) (cons r a))
(define (real-part z)
(mul (magnitude z) (cosine (angle z))))
(define (imag-part z)
(mul (magnitude z) (sine (angle z))))
(define (make-from-real-imag x y)
(cons (square-root (add (square x) (square y)))
(arctan y x)))
(define pi (make-scheme-number (* 2 (asin 1))))
(define (negate z)
(make-from-mag-ang
(magnitude z)
(add pi (angle z))))
;; interface to the rest of the system
(define (tag x) (attach-tag 'polar x))
(put 'real-part '(polar) real-part)
(put 'imag-part '(polar) imag-part)
(put 'magnitude '(polar) magnitude)
(put 'angle '(polar) angle)
(put 'negate '(polar)
(lambda (z) (tag (negate z))))
;;(put 'project '(polar)
;;(lambda (z) real-part z))
(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)
(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 (add (real-part z1) (real-part z2))
(add (imag-part z1) (imag-part z2))))
(define (sub-complex z1 z2)
(make-from-real-imag (sub (real-part z1) (real-part z2))
(sub (imag-part z1) (imag-part z2))))
(define (mul-complex z1 z2)
(make-from-mag-ang (mul (magnitude z1) (magnitude z2))
(add (angle z1) (angle z2))))
(define (div-complex z1 z2)
(make-from-mag-ang (div (magnitude z1) (magnitude z2))
(sub (angle z1) (angle z2))))
;; interface to rest of the system
(define (tag z) (attach-tag 'complex z))
(put 'add '(complex complex)
(lambda (z1 z2) (tag (add-complex z1 z2))))
(put 'add3 '(complex complex complex)
(lambda (z1 z2 z3) (tag (add-complex z1 (add-complex z2 z3)))))
(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 'equ? '(complex complex)
(lambda (z1 z2)
(and (equ? (real-part z1) (real-part z2))
(equ? (imag-part z1) (imag-part z2)))))
(put '=zero? '(complex)
(lambda (z) (equ? (magnitude z) (make-scheme-number 0))))
;;(put 'negate '(complex)
;;(lambda (z) (tag (negate z))))
(put 'negate '(complex)
(lambda (z)
(tag
(make-from-real-imag
(mul (real-part z) (make-scheme-number -1))
(mul (imag-part z) (make-scheme-number -1))))))
(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))))
(put-coercion 'complex 'scheme-number
(lambda (z)
(real-part z)))
'done)
;; *incomplete* skeleton of package
(define (install-polynomial-package)
;; internal procedures
;; representation of poly
(define (make-poly variable term-list)
(cons variable term-list))
(define (variable p) (car p))
(define (term-list p) (cdr p))
;;[procedures same-variable? and variable? from section 2.3.2]
(define (variable? x) (symbol? x))
(define (same-variable? v1 v2)
(and (variable? v1) (variable? v2) (eq? v1 v2)))
;;(define (add-poly p1 p2) ... )
;;[procedures used by add-poly]
(define (add-poly p1 p2)
(if (same-variable? (variable p1) (variable p2))
(make-poly (variable p1)
(add (term-list p1)
(term-list p2)))
(error "Polys not in same var -- ADD-POLY"
(list p1 p2))))
;;(define (mul-poly p1 p2) ... )
;;[procedures used by mul-poly]
(define (mul-poly p1 p2)
(if (same-variable? (variable p1) (variable p2))
(make-poly (variable p1)
(mul (term-list p1)
(term-list p2)))
(error "Polys not in same var -- MUL-POLY"
(list p1 p2))))
(define (div-poly p1 p2)
(if (same-variable? (variable p1) (variable p2))
(let ((quotient-remainder
(div (term-list p1)
(term-list p2))))
(list
(make-poly (variable p1)
(car quotient-remainder))
(make-poly (variable p1)
(cadr quotient-remainder))))
(error "Polys not in same var -- DIV-POLY"
(list p1 p2))))
(define (gcd-poly p1 p2)
(if (same-variable? (variable p1) (variable p2))
(make-poly (variable p1)
(greatest-common-divisor
(term-list p1)
(term-list p2)))))
(define (reduce-poly p1 p2)
(let ((reduced-termlist
(reduce (term-list p1)
(term-list p2))))
(if (same-variable? (variable p1) (variable p2))
(list
(make-poly (variable p1) (car reduced-termlist))
(make-poly (variable p1) (cadr reduced-termlist))))))
(define (negate-poly p)
(make-poly (variable p)
(negate (term-list p))))
(define (=zero-poly? p)
(let ((terms (term-list p)))
(if (null? terms)
#t
(and (= (coeff (first-term terms)) 0)
(=zero-poly? (rest-terms terms))))))
;; interface to rest of the system
(define (tag p) (attach-tag 'polynomial p))
(put 'add '(polynomial polynomial)
(lambda (p1 p2) (tag (add-poly p1 p2))))
(put 'sub '(polynomial polynomial)
(lambda (p1 p2) (tag (add-poly p1 (negate-poly p2)))))
(put 'mul '(polynomial polynomial)
(lambda (p1 p2) (tag (mul-poly p1 p2))))
(put 'div '(polynomial polynomial)
(lambda (p1 p2)
(let ((quotient-remainder (div-poly p1 p2)))
(list
(tag (car quotient-remainder))
(tag (cadr quotient-remainder))))))
(put 'greatest-common-divisor '(polynomial polynomial)
(lambda (p1 p2) (tag (gcd-poly p1 p2))))
(put 'reduce '(polynomial polynomial)
(lambda (p1 p2)
(let ((reduced (reduce-poly p1 p2)))
(list
(tag (car reduced))
(tag (cadr reduced))))))
(put 'negate '(polynomial)
(lambda (p) (tag (negate-poly p))))
(put '=zero? '(polynomial)
(lambda (p) (empty-termlist? (term-list p))))
(put 'make 'polynomial
(lambda (var terms) (tag (make-poly var terms))))
'done)
(define (install-termlist-package)
(define (tag l) (attach-tag 'termlist l))
(define (add-terms L1 L2)
(cond ((empty-termlist? L1) L2)
((empty-termlist? L2) L1)
(else
(let ((t1 (first-term L1)) (t2 (first-term L2)))
(cond ((> (order t1) (order t2))
(adjoin-term
t1 (add-terms (rest-terms L1) L2)))
((< (order t1) (order t2))
(adjoin-term
t2 (add-terms L1 (rest-terms L2))))
(else
(adjoin-term
(make-term (order t1)
(add (coeff t1) (coeff t2)))
(add-terms (rest-terms L1)
(rest-terms L2)))))))))
(define (sub-terms L1 L2)
(add-terms L1 (negate L2)))
(define (mul-terms L1 L2)
(if (empty-termlist? L1)
((get 'the-empty-termlist (type-tag L2)))
(add-terms (mul-term-by-all-terms (first-term L1) L2)
(mul-terms (rest-terms L1)
L2))))
(define (mul-term-by-all-terms t1 L)
(if (empty-termlist? L)
((get 'the-empty-termlist (type-tag L)))
(let ((t2 (first-term L)))
(adjoin-term
(make-term (+ (order t1) (order t2))
(mul (coeff t1) (coeff t2)))
(mul-term-by-all-terms t1 (rest-terms L))))))
(define (div-terms L1 L2)
(if (empty-termlist? L1)
(list ((get 'the-empty-termlist (type-tag L1)))
((get 'the-empty-termlist (type-tag L2))))
(let ((t1 (first-term L1))
(t2 (first-term L2)))
(if (> (order t2) (order t1))
(list ((get 'the-empty-termlist (type-tag L1))) L1)
(let ((new-c (div (coeff t1) (coeff t2)))
(new-o (- (order t1) (order t2))))
(let ((rest-of-result
(div-terms
(sub-terms
L1
(mul-term-by-all-terms
(make-term new-o new-c)
L2))
L2))) ;compute rest of result recursively
(cons
(adjoin-term
(make-term new-o new-c)
(car rest-of-result))
(cdr rest-of-result)))))))) ;form complete result
(define (remainder-terms L1 L2)
(cadr (div-terms L1 L2)))
(define (mul-terms-by-scalar termlist s)
(mul-terms
termlist
(adjoin-term
(make-term 0 s)
((get 'the-empty-termlist (type-tag termlist))))))
(define (div-terms-by-scalar termlist s)
(mul-terms-by-scalar termlist (/ 1 s)))
(define (pseudo-remainder-terms L1 L2)
(let* ((c (coeff (first-term L2)))
(o1 (order (first-term L1)))
(o2 (order (first-term L2)))
(i-factor (exp c
(+ 1 (- o1 o2))))) ;; Only works for integer coefficients, so use non-generic
(cadr (div-terms
(mul-terms
(adjoin-term
(make-term 0 i-factor)
((get 'the-empty-termlist (type-tag L1))))
L1)
L2))))
(define (coeff-gcd termlist)
(if (empty-termlist? termlist)
0
(gcd (coeff (first-term termlist))
(coeff-gcd (rest-terms termlist)))))
(define (gcd-terms a b)
(if (empty-termlist? b)
(div-terms-by-scalar a (coeff-gcd a))
(gcd-terms b (pseudo-remainder-terms a b))))
(define (reduce-terms n d)
(let* ((term-gcd (gcd-terms n d))
(o1 (max (order n) (order d)))
(o2 (order term-gcd))
(i-factor (exp
(coeff (first-term term-gcd))
(+ 1 (- o1 o2)))))
(list
;; Dividing by the gcd will give remainder zero, so we are only
;; interested in the quotient
(car (div-terms (mul-terms-by-scalar n i-factor) term-gcd))
(car (div-terms (mul-terms-by-scalar d i-factor) term-gcd)))))
;; Constructors
(put 'make-sparse-empty-termlist 'termlist
(lambda ()
(tag ((get 'the-empty-termlist 'sparse)))))
(put 'make-sparse-termlist 'termlist
(lambda (term)
(tag ((get 'make-from-term 'sparse) term))))
(put 'make-dense-empty-termlist 'termlist
(lambda ()
(tag ((get 'the-empty-termlist 'dense)))))
(put 'make-dense-termlist 'termlist
(lambda (term)
(tag ((get 'make-from-term 'dense) term))))
;; Term will have its tag removed by apply-generic. This needs to be
;; re-added for the next apply-generic call (needed to step into the
;; appropriate termlist implementation).
(put 'adjoin-term '(term termlist)
(lambda (term termlist)
(tag
(adjoin-term (apply make-term term)
termlist))))
;; Selectors
(put 'first-term '(termlist) first-term)
(put 'rest-terms '(termlist)
(lambda (termlist)
(tag (rest-terms termlist))))
(put 'empty-termlist? '(termlist) empty-termlist?)
;; Operators
(put 'add '(termlist termlist)
(lambda (L1 L2)
(tag (add-terms L1 L2))))
(put 'sub '(termlist termlist)
(lambda (L1 L2)
(tag (sub-terms L1 L2))))
(put 'mul '(termlist termlist)
(lambda (L1 L2)
(tag (mul-terms L1 L2))))
(put 'div '(termlist termlist)
(lambda (L1 L2)
(let ((quotient-remainder (div-terms L1 L2)))
(list (tag (car quotient-remainder))
(tag (cdr quotient-remainder))))))
(put 'negate '(termlist)
(lambda (L)
(tag (negate L))))
(put 'greatest-common-divisor '(termlist termlist)
(lambda (L1 L2)
(tag (gcd-terms L1 L2))))
(put 'reduce '(termlist termlist)
(lambda (L1 L2)
(let ((reduced-termlist
(reduce-terms L1 L2)))
(list (tag (car reduced-termlist))
(tag (cadr reduced-termlist))))))
'done)
(define (install-sparse-package)
;; Make a termlist from a single term
(define (make-termlist term)
(list (contents term)))
;; representation of terms and term lists
;;[procedures adjoin-term ... coeff from text below]
(define (adjoin-term term term-list)
(if (=zero? (coeff term))
term-list
(cons (contents term) term-list)))
(define (negate-termlist term-list)
(map (lambda (t) (list (car t) (negate (cadr t))))
term-list))
(define (the-empty-termlist) '())
(define (empty-termlist? l) (null? l))
(define (tag l) (attach-tag 'sparse l))
;; Constructors
(put 'make-from-term 'sparse
(lambda (term) (tag (make-termlist term))))
(put 'the-empty-termlist 'sparse
(lambda () (tag (the-empty-termlist))))
(put 'adjoin-term '(term sparse)
(lambda (term termlist)
(tag (adjoin-term (apply make-term term) termlist))))
;; Selectors
(put 'first-term '(sparse)
(lambda (termlist)
(if (pair? termlist)
(make-term (caar termlist)
(cadar termlist))
(make-term 0 0))))
(put 'rest-terms '(sparse)
(lambda (termlist)
(tag (cdr termlist))))
(put 'empty-termlist? '(sparse) empty-termlist?)
(put 'order '(sparse)
(lambda (termlist)
(if (pair? termlist)
(caar termlist)
0)))
;; Operators
(put 'negate '(sparse)
(lambda (termlist)
(tag (negate-termlist termlist))))
'done)
(define (install-dense-package)
(define (highest-order-term term-list)
(- (length term-list) 1))
(define empty-termlist? null?)
(define (adjoin-term term term-list)
(define (zero-pad order term-list)
(if (= order (highest-order-term term-list))
term-list
(zero-pad order (cons 0 term-list))))
(define (strip-leading-zeros term-list)
(cond ((null? term-list) '())
((not (= (car term-list) 0))
term-list)
(else strip-leading-zeros (cdr term-list))))
(let ((term-list-order (highest-order-term term-list)))
(cond ((and (null? term-list)
(= (order term) 0))
(list (coeff term)))
((= (coeff term) 0)
(strip-leading-zeros term-list))
((= (order term) (+ term-list-order 1))
(cons (coeff term) term-list))
(else
(cons (coeff term)
(zero-pad
(- (order term) 1)
term-list))))))
(define (negate terms)
(map - terms))
(define (tag l) (attach-tag 'dense l))
;; Constructors
(put 'the-empty-termlist 'dense
(lambda () (tag '())))
(put 'adjoin-term '(term dense)
(lambda (term termlist)
(tag (adjoin-term
(apply make-term term)
termlist))))
;; Selectors
(put 'first-term '(dense)
(lambda (termlist)
(make-term
(highest-order-term termlist)
(car termlist))))
(put 'rest-terms '(dense)
(lambda (termlist)
(tag (cdr termlist))))
(put 'empty-termlist? '(dense)
empty-termlist?)
(put 'order '(dense) highest-order-term)
;; Operators
(put 'negate '(dense)
(lambda (termlist)
(tag (negate termlist))))
'done)
(define (install-term-package)
(define (make-term order coeff)
(tag (list order coeff)))
(define (negate-term term)
(make-term (order term)
(negate (coeff term))))
(define (tag t) (attach-tag 'term t))
(put 'make 'term make-term)
(put 'order '(term) car)
(put 'coeff '(term) cadr)
(put 'negate '(term) negate-term)
'done)
;; Generic constructors
(define (make-scheme-number n)
((get 'make 'scheme-number) n))
(define (make-integer x)
((get 'make 'integer) x))
(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))
(define (make-polynomial var terms)
((get 'make 'polynomial) var terms))
(define (make-term order coeff)
((get 'make 'term) order coeff))
(define (make-sparse-termlist sparse-terms)
((get 'make-sparse-termlist 'termlist) sparse-terms))
(define (make-sparse-empty-termlist)
((get 'make-sparse-empty-termlist 'termlist)))
(define (make-dense-termlist dense-terms)
((get 'make-dense-termlist 'termlist) dense-terms))
(define (make-dense-empty-termlist)
((get 'make-dense-empty-termlist 'termlist)))
;; Generic selectors
(define (real-part z) (apply-generic 'real-part z))
(define (imag-part z) (apply-generic 'imag-part z))
(define (magnitude z) (apply-generic 'magnitude z))
(define (angle z) (apply-generic 'angle z))
(define (order t) (apply-generic 'order t))
(define (coeff t) (apply-generic 'coeff t))
(put 'real-part '(complex) real-part)
(put 'imag-part '(complex) imag-part)
(put 'magnitude '(complex) magnitude)
(put 'angle '(complex) angle)
(define (first-term termlist)
(apply-generic 'first-term termlist))
(define (rest-terms termlist)
(apply-generic 'rest-terms termlist))
;; Generic operators
(define (add x y) (apply-generic 'add x y))
(define (add3 x y z) (apply-generic 'add3 x y z))
(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 (sine x) (apply-generic 'sine x))
(define (cosine x) (apply-generic 'cosine x))
(define (square-root x) (apply-generic 'square-root x))
(define (arctan x y) (apply-generic 'arctan x y))
(define (negate x) (apply-generic 'negate x))
(define (square x) (mul x x))
(define (greatest-common-divisor a b)
(apply-generic 'greatest-common-divisor a b))
(define (reduce a b)
(apply-generic 'reduce a b))
(define (equ? x y) (apply-generic 'equ? x y))
;;(put 'equ? '(complex complex) equ?)
(define (=zero? x) (apply-generic '=zero? x))
(define (exp x y) (apply-generic 'exp x y))
(define (adjoin-term term termlist)
(apply-generic 'adjoin-term term termlist))
(define (empty-termlist? termlist)
(apply-generic 'empty-termlist? termlist))
(define (drop x)
(and (type-tagged? x)
(let ((p (project x)))
(cond ((null? p)
x)
((equ? x (raise p))
(drop p))
(else
x)))))
(install-scheme-number-package)
(install-integer-package)
(install-rational-package)
(install-polar-package)
(install-rectangular-package)
(install-complex-package)
(install-termlist-package)
(install-sparse-package)
(install-dense-package)
(install-term-package)
(install-polynomial-package)
;; Coercion
(define (scheme-number->complex n)
(make-complex-from-real-imag (contents n) 0))
(define (scheme-number->rational n)
(make-rational n 1))
(define (scheme-number->scheme-number n) n)
(define (complex->complex z) z)
;; 2.81a: If we call exp with two complex arguments, apply-generic fails to find an
;; operation with the correct argument list, so tries to coerce t1->t2.
;; b: If the arguments are of the same type, and if there isn't an operation defined for
;; that combination of arguments, then apply-generic will try to coerce a type to itself,
;; so there is a need change it.
;; 2.82: The approach of coercing to a common type would not work where there is a function
;; that takes mixed types. For example, if we have an operator to add a complex to a rational.
;; It isn't possible to coerce from one to the other, so a mixed-type operator is needed, and this
;; approach would fail.
;; 2.95
;;(define p1 (make-polynomial 'x
;; (adjoin-term (make-term 2 1)
;; (adjoin-term (make-term 1 -2)
;; (adjoin-term (make-term 0 1)
;; (make-sparse-empty-termlist))))))
;;(define p2 (make-polynomial 'x
;; (adjoin-term (make-term 2 11)
;; (adjoin-term (make-term 0 7)
;; (make-sparse-empty-termlist)))))
;;(define p3 (make-polynomial 'x (adjoin-term (make-term 1 13)
;; (adjoin-term (make-term 0 5)
;; (make-sparse-empty-termlist)))))
;;(define q1 (mul p1 p2))
;;(define q2 (mul p1 p3))
;; GCD of q1 and q2 is not p1: it is a polynomial with rational coefficients because
;; of the division of coefficients.
;; 2.97
(define p1 (make-polynomial 'x
(adjoin-term (make-term 1 1)
(adjoin-term (make-term 0 1)
(make-sparse-empty-termlist)))))
(define p2 (make-polynomial 'x
(adjoin-term (make-term 3 1)
(adjoin-term (make-term 0 -1)
(make-sparse-empty-termlist)))))
(define p3 (make-polynomial 'x
(adjoin-term (make-term 1 1)
(make-sparse-empty-termlist))))
(define p4 (make-polynomial 'x
(adjoin-term (make-term 2 1)
(adjoin-term (make-term 0 -1)
(make-sparse-empty-termlist)))))
(define rf1 (make-rational p1 p2))
(define rf2 (make-rational p3 p4))