CS37 Lab 04a: Number Representations

Due 11:59pm Sunday, February 24
Introduction

This is an optional lab, adapted from Section 2.5 in the textbook. If you choose to complete it, it is due on February 24th.

Run update37 to download the skeleton file (numbers.rkt) for your lab and to re-download the hash table file (table.rkt) that we used in class. The numbers.rkt file contains a complete working implementation of the generic number package from Section 2.5.1.

There are only two questions in this lab, Exercise 2.77 and Exercise 2.79 from the text book. You will need to have read (or read along with) Section 2.5 in order for much of this lab to make sense. The questions are asked inline to the discussion below.

Numbers of various types

We want to make a 'package' (as defined Section 2.5 of the text) that not only handles complex numbers in two forms -- rectangular and polar -- but also regular numbers and rational numbers as defined in Section 2.1.1.

Run the code in numbers.rkt and try the following:

> seven
(scheme-number . 7)
> ten
(scheme-number . 10)
> five
(scheme-number . 5)
> b30
(complex rectangular 1.7320508075688772 . 1)
> b45
(complex rectangular 4 . 4)
> a45
(complex polar 5.656854249492381 . 0.7853981633974483)
> (mul a45 b30)
(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. Let's look at this file in more detail.

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

> ten
(scheme-number . 10)
> (square ten)
reference to an identifier before its definition: square
This is not terribly surprising, since 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 (lambda (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))
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. Now you should go ahead and add square for complex numbers.


Let's turn our attention to Exercise 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 ; given (complex rectangular 3 . 4)
> 
This isn't even what the text says. The reason is that Racket 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 (lambda (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 complete the exercise. 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
> 

Be sure to answer the last part of the question:

Describe in detail why this works. As an example, trace through all the procedures called in evaluating the expression (magnitude z) where z is the object shown in figure 2.24. In particular, how many times is apply-generic invoked? What procedure is dispatched to in each case?


Complete Exercise 2.79:

Define a generic equality predicate equ? that tests the equality of two numbers, and install it in the generic arithmetic package. This operation should work for ordinary numbers, rational numbers, and complex numbers.


There are no other questions to answer for this lab, but Section 2.5.2 is a really interesting read and I'd encourage you to read through it at some point.

Submit

Once you are satisfied with your programs, hand them in by using handin37. Be sure that both you and your partner run handin37, selecting your respective partner.