sicp/2_78.rkt

680 lines
22 KiB
Racket
Raw Normal View History

2022-02-07 11:46:42 +00:00
#lang sicp
;;;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!))
2022-02-07 19:45:40 +00:00
(define coercion-table (make-table))
(define get-coercion (coercion-table 'lookup-proc))
(define put-coercion (coercion-table 'insert-proc!))
2022-02-07 11:46:42 +00:00
;; Apply generic
2022-02-07 19:45:40 +00:00
;;(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))))))
2022-02-18 20:41:52 +00:00
;; Number tower. Ignores possibility of one of the arguments
;; not being in the tower. Maybe fix later.
(define (lower-in-tower? t1 t2)
2022-03-06 22:41:01 +00:00
(define tower '(integer rational scheme-number complex))
2022-02-18 20:41:52 +00:00
(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)))
2022-02-08 22:38:46 +00:00
(define tower '(integer rational scheme-number complex))
(define (next-type type tower)
(cond ((or (null? tower)
(null? (cdr tower))) '())
((eq? type (car tower)) (cadr tower))
(else (next-type type (cdr tower)))))
(define (transform-type x tower)
(and (type-tagged? x)
(let* ((type (type-tag x))
(proj-type (next-type type tower)))
(if proj-type
((get-coercion type proj-type) (contents x))
'()))))
(define (raise x)
(transform-type x tower))
(define (project x)
(transform-type x (reverse tower)))
2022-02-07 11:46:42 +00:00
(define (apply-generic op . args)
2022-02-08 22:38:46 +00:00
(define (coerce-to t)
2022-02-18 20:41:52 +00:00
(letrec ((coerce (lambda (arg)
(cond ((eq? (type-tag arg) t) arg)
((lower-in-tower?
(type-tag arg)
t)
(coerce (raise arg)))
(else '())))))
coerce))
2022-02-08 22:38:46 +00:00
(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
(begin
(display "Applying ")
(display (list op args))
(newline)
(apply proc (map contents args)))
(if (> (length args) 1)
(let ((coerced-args (coerce-args args type-tags)))
(if (not (null? coerced-args))
(begin
(display "Applying with coerced args ")
(display (list op coerced-args))
(newline)
(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)))))
(if (and (pair? result)
(not (or (eq? op 'raise)
(eq? op 'project)
(eq? op 'real-part)
(eq? op 'imag-part)
(eq? op 'angle)
(eq? op 'magnitude))))
(begin
(display "Drop ")
(display result)
(newline)
(drop result))
(begin
(display "Result ")
(display result)
(newline)
result))))
2022-02-08 22:38:46 +00:00
;; scheme-number is for reals only
(define (scheme-number? x)
(and (real? x)
(not (integer? x))))
(define (attach-tag type-tag contents)
(cond ((or (scheme-number? contents)
(integer? contents))
contents)
(else (cons type-tag contents))))
(define (type-tag datum)
(cond ((scheme-number? datum) 'scheme-number)
((integer? datum) 'integer)
((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))))
2022-02-07 11:46:42 +00:00
;; Scheme numbers
(define (install-scheme-number-package)
(define (tag x)
(attach-tag 'scheme-number x))
2022-02-07 19:45:40 +00:00
(define (exp x y) (apply-generic 'exp x y))
2022-02-07 11:46:42 +00:00
(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))))
2022-02-07 11:46:42 +00:00
(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)
2022-03-06 22:41:01 +00:00
(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))))
2022-02-07 11:46:42 +00:00
(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)))
2022-02-07 19:45:40 +00:00
(put 'exp '(scheme-number scheme-number)
(lambda (x y) (tag (expt x y))))
2022-02-07 11:46:42 +00:00
(put 'make 'scheme-number
(lambda (x) (tag x)))
'done)
2022-02-13 20:10:54 +00:00
;; Integers
(define (install-integer-package)
(define (tag x)
(attach-tag 'integer x))
(define (raise-int x)
(make-rational x 1))
2022-02-13 20:10:54 +00:00
2022-03-10 22:37:16 +00:00
(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))))
2022-03-10 22:37:16 +00:00
(put 'equ? '(integer integer)
(lambda (x y)
(= x y)))
(put-coercion 'integer 'rational raise-int)
2022-02-13 20:10:54 +00:00
(put 'make 'integer
2022-03-10 22:37:16 +00:00
(lambda (x) (tag (round x))))
'done)
2022-02-13 20:10:54 +00:00
2022-02-07 11:46:42 +00:00
;; Rationals
(define (install-rational-package)
;; internal procedures
(define (numer x) (car x))
(define (denom x) (cdr x))
(define (make-rat n d)
(let ((g (gcd n d)))
(cons (/ 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 (eq-rat x y)
(and (= (numer x) (numer y))
(= (denom x) (denom y))))
(define (raise-rat x)
2022-03-06 22:41:01 +00:00
(make-scheme-number (/ (numer x) (denom x))))
2022-02-07 11:46:42 +00:00
;; 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)))))
2022-02-07 11:46:42 +00:00
(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))))
2022-02-07 11:46:42 +00:00
(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)))))
2022-02-07 11:46:42 +00:00
(put 'make 'rational
2022-02-07 11:46:42 +00:00
(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)))))
2022-02-07 11:46:42 +00:00
(define (angle z)
(arctan (imag-part z) (real-part z)))
2022-02-07 11:46:42 +00:00
(define (make-from-mag-ang r a)
(cons (mul r (cosine a))
(mul r (sine a))))
(define (negate z)
(make-from-real-imag
2022-03-06 22:41:01 +00:00
(mul (make-scheme-number -1) (real-part z))
(mul (make-scheme-number -1) (imag-part z))))
2022-02-07 11:46:42 +00:00
;; 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)))
2022-02-07 11:46:42 +00:00
(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))))
2022-02-07 11:46:42 +00:00
(define (imag-part z)
(mul (magnitude z) (sine (angle z))))
2022-02-07 11:46:42 +00:00
(define (make-from-real-imag x y)
(cons (square-root (add (square x) (square y)))
(arctan y x)))
2022-03-06 22:41:01 +00:00
(define pi (make-scheme-number (* 2 (asin 1))))
(define (negate z)
(make-from-mag-ang
(magnitude z)
(add pi (angle z))))
2022-02-07 11:46:42 +00:00
;; 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))
2022-02-07 11:46:42 +00:00
(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))))
2022-02-07 11:46:42 +00:00
(define (sub-complex z1 z2)
(make-from-real-imag (sub (real-part z1) (real-part z2))
(sub (imag-part z1) (imag-part z2))))
2022-02-07 11:46:42 +00:00
(define (mul-complex z1 z2)
(make-from-mag-ang (mul (magnitude z1) (magnitude z2))
(add (angle z1) (angle z2))))
2022-02-07 11:46:42 +00:00
(define (div-complex z1 z2)
(make-from-mag-ang (div (magnitude z1) (magnitude z2))
(sub (angle z1) (angle z2))))
2022-02-07 11:46:42 +00:00
;; 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)))))
2022-02-07 11:46:42 +00:00
(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)))))
2022-02-07 11:46:42 +00:00
(put '=zero? '(complex)
2022-03-06 22:41:01 +00:00
(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
2022-03-06 22:41:01 +00:00
(mul (real-part z) (make-scheme-number -1))
(mul (imag-part z) (make-scheme-number -1))))))
2022-02-07 11:46:42 +00:00
(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)))
2022-02-07 11:46:42 +00:00
'done)
2022-03-01 21:57:43 +00:00
;; *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)))
;; 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 term term-list)))
(define (the-empty-termlist) '())
(define (first-term term-list) (car term-list))
(define (rest-terms term-list) (cdr term-list))
(define (empty-termlist? term-list) (null? term-list))
(define (make-term order coeff) (list order coeff))
(define (order term) (car term))
(define (coeff term) (cadr term))
(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 (mul-terms L1 L2)
(if (empty-termlist? L1)
(the-empty-termlist)
(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)
(the-empty-termlist)
(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 (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-terms (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-terms (term-list p1)
(term-list p2)))
(error "Polys not in same var -- MUL-POLY"
(list p1 p2))))
(define (=zero-poly? p)
(let ((terms (term-list p)))
(if (null? terms)
#t
(and (= (coeff (first-term terms)) 0)
(=zero-poly? (rest-terms terms))))))
2022-03-01 21:57:43 +00:00
;; 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 'mul '(polynomial polynomial)
(lambda (p1 p2) (tag (mul-poly p1 p2))))
(put '=zero? '(polynomial)
(lambda (p) (empty-termlist? (term-list p))))
(put 'make 'polynomial
(lambda (var terms) (tag (make-poly var terms))))
'done)
2022-02-07 11:46:42 +00:00
;; Generic constructors
(define (make-scheme-number n)
((get 'make 'scheme-number) n))
2022-02-13 20:10:54 +00:00
(define (make-integer x)
((get 'make 'integer) x))
2022-02-07 11:46:42 +00:00
(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))
2022-03-01 21:57:43 +00:00
(define (make-polynomial var terms)
((get 'make 'polynomial) var terms))
2022-02-07 11:46:42 +00:00
;; 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))
(put 'real-part '(complex) real-part)
(put 'imag-part '(complex) imag-part)
(put 'magnitude '(complex) magnitude)
(put 'angle '(complex) angle)
;; Generic operators
(define (add x y) (apply-generic 'add x y))
2022-02-18 20:41:52 +00:00
(define (add3 x y z) (apply-generic 'add3 x y z))
2022-02-07 11:46:42 +00:00
(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))
2022-02-07 11:46:42 +00:00
(define (equ? x y) (apply-generic 'equ? x y))
;;(put 'equ? '(complex complex) equ?)
(define (=zero? x) (apply-generic '=zero? x))
2022-02-07 19:45:40 +00:00
(define (exp x y) (apply-generic 'exp x y))
2022-02-07 11:46:42 +00:00
(define (drop x)
(and (type-tagged? x)
(let ((p (project x)))
(begin
(display "drop: ")
(display x)
(newline)
(cond ((null? p)
(display "drop: p Null ")
(newline)
x)
((equ? x (raise p))
(display "drop: x equal to raise p, dropping ")
(display p)
(newline)
(drop p))
(else
(display "drop: returning ")
(display x)
(newline)
x))))))
2022-02-13 20:10:54 +00:00
2022-02-07 11:46:42 +00:00
(install-scheme-number-package)
2022-02-13 20:10:54 +00:00
(install-integer-package)
2022-02-07 11:46:42 +00:00
(install-rational-package)
(install-polar-package)
(install-rectangular-package)
(install-complex-package)
2022-03-01 21:57:43 +00:00
(install-polynomial-package)
2022-02-07 11:46:42 +00:00
2022-02-07 19:45:40 +00:00
;; Coercion
(define (scheme-number->complex n)
(make-complex-from-real-imag (contents n) 0))
(define (scheme-number->rational n)
(make-rational n 1))
2022-02-07 19:45:40 +00:00
(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.
2022-02-13 20:10:54 +00:00