CS 22

Clab 15: More with Multiple representations
Message Passing and Data-driven genericity


For homework, you played around with tagged representations and a table based complex number package that allowed two different representations of complex numbers. If you don't already have a week5 subdirectory, make one now and when you save today, save into it.


Here are some values from last clab to help us test our table driven functions. ;; some numbers in simple rectangular form > cpx30 (1.7320508075688772 . 1) > cpx45 (4 . 4) > cpx60 (2 . 3.4641016151377544) > ;; same number in simple polar form > acpx30 (1.9999999999999998 . 0.5235987755982989) > acpx45 (5.656854249492381 . 0.7853981633974483) > acpx60 (3.9999999999999996 . 1.0471975511965976) > ;; a couple of constants of interest > root3 1.7320508075688772 > pi 3.141592653589793 > pidiv6 0.5235987755982988 > pidiv3 1.0471975511965976 > pidiv2 1.5707963267948966 > ;; some correct arithmetic in simple rectangular form > (add-complex cpx30 cpx45) (5.732050807568877 . 5) > (add-complex cpx30 cpx60) (3.732050807568877 . 4.464101615137754) > (mul-complex cpx30 cpx45) (2.9282032302755088 . 10.928203230275509) > (mul-complex cpx30 cpx60) (4.898587196589412e-16 . 7.999999999999998) > ;; some correct arithmetic in simple polar form > (add-complex acpx30 acpx45) (7.606339885947184 . 0.7172920193875758) > (add-complex acpx30 acpx60) (5.818625822352818 . 0.8744781864705565) > (mul-complex acpx30 acpx45) (11.31370849898476 . 1.3089969389957472) > (mul-complex acpx30 acpx60) (7.999999999999998 . 1.5707963267948966) Last time we implemented the table driven package and tried the following. >(define new30 (make-from-real-imag 1.7320508075688772 1)) > (define new45 (make-from-mag-ang 5.656854249492381 0.7853981633974483)) > new30 (rectangular 1.7320508075688772 . 1) > new45 (polar 5.656854249492381 . 0.7853981633974483) > (real-part new30) 1.7320508075688772 > (real-part new45) 4.000000000000001 This is looking good so let's do some arithmetic. > (define prod3045 (mul-complex new30 new45)) reference to undefined identifier: mul-complex OOPS. What is the problem? What do we need to do?
We had not put in the arithmetic functions. Since we have appropriate constructors and selectors, we can use the good old functions we had before. ;; the same implementation independent definitions of ;; add, sub, mult, div (define (add-complex z1 z2) (make-from-real-imag (+ (real-part z1) (real-part z2)) (+ (imag-part z1) (imag-part z2)))) (define (sub-complex z1 z2) (make-from-real-imag (- (real-part z1) (real-part z2)) (- (imag-part z1) (imag-part z2)))) (define (mul-complex z1 z2) (make-from-mag-ang (* (magnitude z1) (magnitude z2)) (+ (angle z1) (angle z2)))) (define (div-complex z1 z2) (make-from-mag-ang (/ (magnitude z1) (magnitude z2)) (- (angle z1) (angle z2)))) Putting these in at the top level, we can get: > (define new30 (make-from-real-imag 1.7320508075688772 1)) > (define new45 (make-from-mag-ang 5.656854249492381 0.7853981633974483)) > (define prod3045 (mul-complex new30 new45)) > prod3045 (polar 11.31370849898476 . 1.3089969389957472) >

Now we are going to take a different path to obtain genericity. This is called message passing. It is at the heart of object-oriented programming. We create an object which encapsulates data and operators on that data. In this case we will start with a constructor for a complex number in rectangular form that encapsulates its real and imaginary parts and the selectors real-part, imag-part, magnitude, and angle. We could encapsulate arithmetic operators in the object, but your authors' choose to use their good old arithmetic operators from before. Here is the code from the message passing part of section 2.4.3:

;; Message passing (define (make-from-real-imag x y) (define (dispatch op) (cond ((eq? op 'real-part) x) ((eq? op 'imag-part) y) ((eq? op 'magnitude) (sqrt (+ (square x) (square y)))) ((eq? op 'angle) (atan y x)) (else (error "Unknown op -- MAKE-FROM-REAL-IMAG" op)))) dispatch) (define (apply-generic op arg) (arg op)) In order to make convenient use of this (say with the good old arithmetic functions) we'll add some 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)) We'll need square and the good old arithmetic functions. Also, let's add a convenient way to see the value of an object. (define (square x) (* x x)) ;; the same implementation independent definitions of ;; add, sub, mult, div (define (add-complex z1 z2) (make-from-real-imag (+ (real-part z1) (real-part z2)) (+ (imag-part z1) (imag-part z2)))) (define (sub-complex z1 z2) (make-from-real-imag (- (real-part z1) (real-part z2)) (- (imag-part z1) (imag-part z2)))) (define (mul-complex z1 z2) (make-from-mag-ang (* (magnitude z1) (magnitude z2)) (+ (angle z1) (angle z2)))) (define (div-complex z1 z2) (make-from-mag-ang (/ (magnitude z1) (magnitude z2)) (- (angle z1) (angle z2)))) ;;since we can't see components easily, make show functions (define showrect (lambda (z) (cons (real-part z) (imag-part z)))) (define showpolar (lambda (z) (cons (magnitude z) (angle z)))) Save these definitions in a file called msgcpx.scm and execute. Now try some of the following. > (define msg45 (make-from-real-imag 4 4)) > (define msg30 (make-from-real-imag (sqrt 3) 1)) > (showrect msg45) (4 . 4) > (showrect msg30) (1.7320508075688772 . 1) > (showpolar msg30) (1.9999999999999998 . 0.5235987755982989) > (showpolar msg45) (5.656854249492381 . 0.7853981633974483) > (define add3045 (add-complex msg30 msg45)) > (showrect add3045) (5.732050807568877 . 5) > Looks pretty good. But now try. (define mul3045 (mul-complex msg30 msg45)) Oops, there are a number of workarounds but the best is to solve exercise 2.75. You can also construct some numbers in polar form. This is one of your homework problems for next Tuesday. You should be able to do stuff like: > (define msg30 (make-from-real-imag (sqrt 3) 1)) > (define msg45 (make-from-real-imag 4 4)) > (define mul3045 (mul-complex msg30 msg45)) > (showrect mul3045) (2.9282032302755088 . 10.928203230275509) > (showpolar mul3045) (11.31370849898476 . 1.3089969389957472) > after you finish exercise 2.75 at home.


Numbers, numbers, numbers of various types

Back to table driven stuff, but now we want to make a package that not only handles complex numbers in two forms but also scheme numbers and rational numbers as defined in section 2.1.1.

A complete working implementation of the generic number package from SICP section 2.5.1 pp. 189-192 is available in my pub directory:
/home/cfk/pub/cs22/week5/numbs2.scm
Import numbs2.scm to your week5 subdirectory and open it in drscheme. Press execute and try:

> seven (scheme-number . 7) > ten (scheme-number . 10) > five (scheme-number . 5) > genb30 (complex rectangular 1.7320508075688772 . 1) > genb45 (complex rectangular 4 . 4) > gena45 (complex polar 5.656854249492381 . 0.7853981633974483) > (mul gena45 genb30) (complex polar 11.31370849898476 . 1.3089969389957472) > (mul ten five) (scheme-number . 50) > pct75 (rational 3 . 4) > tenr (rational 10 . 1) > (mul pct75 tenr) (rational 15 . 2)

So it appears that we have a working package that can do generic arithmetic. We'll take a tour of numbs2.scm together.

Now, let's try squaring an ordinary number or one of our generic numbers.

> ten (scheme-number . 10) > (square ten) reference to undefined identifier: square Not terribly surprising, we don't have square defined at the top-level. If we want a generic square, we should define it like we did add, sub, etc. So let's add ;; let's add a square operator (define (square x) (apply-generic 'square x)) just after div in our list of generic operators. Now we try again and get: > ten (scheme-number . 10) > (square ten) No method for these types -- APPLY-GENERIC (square (scheme-number)) Well, that's not very encouraging. Then again, we didn't put any square operators in the table in any of the number packages. So why should apply-generic find any? Let's put some in. Try the following in the scheme-number package just after (put 'make ... (put 'square '(scheme-number) (lambda (x) (tag (* x x)))) Try execute and see if you get: > ten (scheme-number . 10) > (square ten) (scheme-number . 100) Looking good. Now all we have to do is put square in the rational and complex packages. Do it first just for rationals and test that to see if you get: > ten (scheme-number . 10) > tenr (rational 10 . 1) > pct75 (rational 3 . 4) > (square ten) (scheme-number . 100) > (square tenr) (rational 100 . 1) > (square pct75) (rational 9 . 16)

Ok. It works for scheme-numbers and rationals. Only now should you go ahead and do for complex. But do that at home or later if there is time.


Let's turn our attention to ex 2.77.
We can construct Louis' number and try magnitude. We get

> (define louis (make-complex-from-real-imag 3 4)) > louis (complex rectangular 3 . 4) > (magnitude louis) magnitude: expects argument of type <number>; given (complex rectangular 3 . 4) > This isn't even what the text says. The reason is that drscheme has a magnitude function that is built-in. We need to define a magnitude function for our generic package that will override the built-in one. We expect to define our generic magnitude in a fashion similar to how we did square. So let's add: ;; the new magnitude operator (define (magnitude x) (apply-generic 'magnitude x)) just after the top-level square we added. Now we get: > (define louis (make-complex-from-real-imag 3 4)) > louis (complex rectangular 3 . 4) > (magnitude louis) No method for these types -- APPLY-GENERIC (magnitude (complex)) > This is what the problem in the text says, so now you can do the problem. :-) You need to decide where to put the code suggested by Alyssa. Remember that at any given level, internal definitions have to precede anything else, so make sure you add this code after any internal definitions in the complex package. If you get it in the right place you ought to be able to get: > (define louis (make-complex-from-real-imag 3 4)) > louis (complex rectangular 3 . 4) > (magnitude louis) 5 >

ask any questions you may have.