(b) Defining square as suggested would require the addition of a square procedure to the table for every possible type of generic number. Each of those procedures would have to be different, because they would receive the internal representation of the number as their argument rather than a generic number (e.g. RepNum rather than Generic-Num). Defining the square procedure in terms of the generic mul procedure allows it to handle all types of generic numbers, including any new ones we define later, with a minimum of hassle.
make-number : RepNum -> Generic-OrdNum negate : RepNum -> Generic-OrdNum zero? : RepNum -> SchBool
;=number : (RepNum,RepNum) -> SchBool (define =number? =) (put 'equ? '(number number) =number?)
rsq ;Value: (rational (number . 120) number . 100)It's box-and-pointer representation is:
_____ _____ _____
--->| | ----->| | ------------->| | -----> 100
|| | | || | | || | |
|~~~~ |~~~~ |~~~~
V V_____ V
'rational | | -----> 120 'number
|| | |
|~~~~
V
'number
;negate-rat : RepRat -> RepRat
(define (negate-rat x)
(make-rat (negate (numer x)) (denom x)))
;negate-rational : RepRat -> Generic-Rational
(define (negate-rational x) (tag (negate-rat x)))
;=zero-rat? : RepRat -> SchBool
(define (=zero-rational? x) (=zero? (numer x)))
;=rational? : (RepRat,RepRat) -> SchBool
(define (=rational? x y) (=zero-rational? (sub-rat x y)))
(put 'negate '(rational) negate-rational)
(put '=zero? '(rational) =zero-rational?)
(put 'equ? '(rational rational) =rational?)
(define (repnum->reprat n)
(make-rat (create-number n) (create-number 1)))
(define (RRmethod->RNmethod method)
(lambda (rat num)
(method rat (repnum->reprat num))))
(put 'add '(rational number) (RRmethod->RNmethod add-rational)) (put 'sub '(rational number) (RRmethod->RNmethod sub-rational)) (put 'mul '(rational number) (RRmethod->RNmethod mul-rational)) (put 'div '(rational number) (RRmethod->RNmethod div-rational)) (put 'equ? '(rational number) (RRmethod->RNmethod =rational?)) (put 'add '(number rational) (RRmethod->NRmethod add-rational)) (put 'sub '(number rational) (RRmethod->NRmethod sub-rational)) (put 'mul '(number rational) (RRmethod->NRmethod mul-rational)) (put 'div '(number rational) (RRmethod->NRmethod div-rational)) (put 'equ? '(number rational) (RRmethod->NRmethod =rational?))
csq ;Value: (complex (number . -35) number . -12)It's box-and-pointer representation is:
_____ _____ _____
--->| | ----->| | ------------->| | -----> -12
|| | | || | | || | |
|~~~~ |~~~~ |~~~~
V V_____ V
'complex | | -----> -35 'number
|| | |
|~~~~
V
'number
;negate-com : RepRat -> RepRat
(define (negate-com x)
(make-com (negate (real x)) (negate (imag x))))
;negate-complex : RepCom -> Generic-Complex
(define (negate-complex x) (tag (negate-com x)))
;=zero-com? : RepCom -> SchBool
(define (=zero-complex? x) (and (=zero? (real x)) (=zero? (imag x))))
;=Complex? : (RepCom,RepCom) -> SchBool
(define (=complex? x y) (and (equ? (real x) (real y))
(equ? (imag x) (imag y))))
(put 'negate '(complex) negate-complex)
(put '=zero? '(complex) =zero-complex?)
(put 'equ? '(complex complex) =complex?)
(define (repnum->repcom n)
(make-com (create-number n) (create-number 0)))
(define (CCmethod->CNmethod method)
(lambda (com num)
(method com (repnum->repcom num))))
(put 'add '(complex number) (CCmethod->CNmethod add-complex)) (put 'sub '(complex number) (CCmethod->CNmethod sub-complex)) (put 'mul '(complex number) (CCmethod->CNmethod mul-complex)) (put 'div '(complex number) (CCmethod->CNmethod div-complex)) (put 'equ? '(complex number) (CCmethod->CNmethod =complex?)) (put 'add '(number complex) (CCmethod->NCmethod add-complex)) (put 'sub '(number complex) (CCmethod->NCmethod sub-complex)) (put 'mul '(number complex) (CCmethod->NCmethod mul-complex)) (put 'div '(number complex) (CCmethod->NCmethod div-complex)) (put 'equ? '(number complex) (CCmethod->NCmethod =complex?))
;Value: (complex (number . .2) number . .6)This represents 1/5 + 3/5 i, which is the correct answer.
If we chose to use the rational number package to represent the dividend, we could do so by calling (create-rational c1+3i n5), and the result would be as follows:
;Value: (rational (complex (number . 1) number . 3) number . 5)This represents the rational number (1+3i)/5. Also a correct answer, where no reduction has been performed.
(define (div-com-new x y)
(let ((com-conj (complex-conjugate y)))
(let ((x-times-com-conj (mul-com x com-conj))
(y-times-com-conj (mul-com y com-conj)))
(make-com (create-rational (real x-times-com-conj)
(real y-times-com-conj))
(create-rational (imag x-times-com-conj)
(real y-times-com-conj)))))))
(put 'div '(complex complex) div-com-new)
The equality operations as we have written them would also fail in the situation of nested complex numbers. This is because the complex =zero? operation depends on both the real and imaginary parts being zero. If those parts contained complex numbers themselves, that would not necessarily be true even when the complex number represented was zero. For instance, (1+i)+(-1-i)i is equal to zero, but our =zero? operation would not detect it as such.
Avoiding this limitation entirely would require a method of examining the two components of a complex number to separate them into real and imaginary parts. However, since those components are themselves generic numbers, finding any complex numbers which might be nested within them would be difficult, especially if we want to maintain support for new types of generic numbers other than the four defined in this probelm set. Instead, then, we simply coerce the two arguments to our rational-complex equality procedures into rational numbers, thus guaranteeing that we do not introduce any new complex numbers with complex components. Our code will still perform incorrectly if the user creates such numbers, but we must rely on the user not to do so.
Since these procedures bridge the Rational and Complex packages they do not fit particularly well into either one of them. Thus we put them in their own package called the Rational-Complex Equality Package:
(define (install-rational-complex-equality-package)
(define (=rat-com rat com)
(equ? (attach-tag 'rational rat)
(create-rational (attach-tag 'complex com)
(create-number 1))))
(define (=com-rat com rat)
(=rat-com rat com))
(put 'equ? '(rational complex) =rat-com)
(put 'equ? '(complex rational) =com-rat)
'done)
(define (map-terms proc tlist)
(if (empty-termlist? tlist)
(make-empty-termlist)
(adjoin-term
(proc (first-term tlist))
(map-terms proc (rest-terms tlist)))))
(define (create-numerical-polynomial var coeffs)
(define (add-orders clist order)
(if (null? clist)
(make-empty-termlist)
(adjoin-term (make-term order
(create-number (car clist)))
(add-orders (cdr clist) (- order 1)))))
(create-polynomial var
(add-orders coeffs (- (length coeffs) 1))))
Once this procedure is defined we can use it to define p1 as follows:
(define p1 (create-numerical-polynomial 'x '(1 5 0 -2)))
(define (negate-poly p)
(make-poly (variable p)
(map-terms (lambda (term)
(make-term (order term)
(negate (coeff term))))
(term-list p))))
(define (negate-polynomial p) (tag (negate-poly p)))
(put 'negate '(polynomial) negate-polynomial)
With the negate operation defined, the polynomial sub
operation is easy:
(define (sub-polynomial p1 p2)
(add-polynomial p1 (negate-poly p2)))
(put 'sub '(polynomial polynomial) sub-polynomial)
The =zero? and equ? operations both depend on the
ability to compare termlists, so we define a helper procedure to do
so:
(define (=termlist? t1 t2)
(cond ((empty-termlist? t1) (empty-termlist? t2))
((empty-termlist? t2) #f)
(else (and (and (= (order (first-term t1))
(order (first-term t2)))
(equ? (coeff (first-term t1))
(coeff (first-term t2))))
(=termlist? (rest-terms t1)
(rest-terms t2))))))
Now the actual predicates are defined easily:
(define (=zero-polynomial? p)
(empty-termlist? (term-list p))) ;Thanks to adjoin-term
(define (=polynomial? p1 p2)
(if (same-variable? (variable p1) (variable p2))
(=termlist? (term-list p1) (term-list p2))
(error "Polys not in same var -- EQ-POLY"
(list p1 p2))))
(put 'equ? '(polynomial polynomial) =polynomial?)
(put '=zero? '(polynomial) =zero-polynomial?)
Note that two polynomials of different variables are treated as
incomparable, such that comparing them with equ? causes an
error. Treating them as always not equal would also be a valid
approach.
(define p2
(create-polynomial
'z
(adjoin-term
(make-term 2 p1)
(adjoin-term
(make-term 1 (create-numerical-polynomial 'x '(5)))
(adjoin-term
(make-term 0 (create-numerical-polynomial 'x '(3)))
(make-empty-termlist))))))
(define (apply-terms termlist generic-number)
(if (empty-termlist? termlist)
(create-number 0)
(add (apply-term (first-term termlist) generic-number)
(apply-terms (rest-terms termlist) generic-number))))
(define (PPmethod->PNmethod proc)
(lambda (p n)
(proc p
(make-poly (variable p)
(adjoin-term
(make-term 0
(attach-tag 'number n))
(make-empty-termlist))))))
(define (PPmethod->NPmethod proc)
(lambda (n p)
(proc (make-poly (variable p)
(adjoin-term
(make-term 0
(attach-tag 'number n))
(make-empty-termlist)))
p)))
(put 'add '(polynomial number) (PPmethod->PNmethod add-polynomial))
(put 'sub '(polynomial number) (PPmethod->PNmethod sub-polynomial))
(put 'mul '(polynomial number) (PPmethod->PNmethod mul-polynomial))
(put 'add '(number polynomial) (PPmethod->NPmethod add-polynomial))
(put 'sub '(number polynomial) (PPmethod->NPmethod sub-polynomial))
(put 'mul '(number polynomial) (PPmethod->NPmethod mul-polynomial))