Com S 342 meeting -*- Outline -*- * Multiple Representations for Abstract Data (2.4) ** motivation different data structures have different trade-offs in efficiency - sets with and without duplicates, constant time insertion, vs. faster deletion and less space - rectangular vs. polar coordinates efficiency of multiplication evolution of systems - on-line systems that change representations must deal with old ones - "open" systems that allow users to define new data component based systems systems built by combining independently-developed components will have several representations for the same type, in general ** problem how to construct "generic procedures", that operate on multiple reps ** overview equip data with type tags that describe the representation Q: what are these type tags like in Java? ** complex number example --------------------------------------------------------- OVERVIEW client programs that use complex numbers ================================================== add-complex, sub-complex, mul-complex, div-complex ================================================== rectangular | polar representation | representation (primitives) --------------------------------------------------------- *** rectangular rep pair of values, as in x+yi, represented by the pair (x . y) *** polar rep magnitude plus angle, as in r*e^{i*A}, with angle A measured counter-clockwise from the x-axis *** math --------------------------------------------------------- if x+yi = r*e^{i*A}, then x = r cos A r = sqrt(x^2 + y^2) y = r sin A A = arctan(y,x) --------------------------------------------------------- arctan(y,x) computes angle whose tangent is y/x, with quadrant determined by signs of y and x *** protocol of complex numbers --------------------------------------------------------- public interface ComplexIF { public double realPart(); public double imagPart(); public double magnitude(); public double angle(); } --------------------------------------------------------- Will also use procedures make-from-real-imag make-from-mag-ang *** implementation of rectangular rep (Ben) ;;; primitives (define (real-part z) (car z)) (define (imag-part z) (cdr z)) (define (magnitude z) (sqrt (+ (square (real-part z)) (square (imag-part z))))) (define (angle z) (atan (imag-part z) (real-part z))) ;;; constructors (define (make-from-real-imag x y) (cons x y)) (define (make-from-mag-ang r a) (cons (* r (cos a)) (* r (sin a)))) *** implementation of polar coordinates (Alyssa) ;;; primitives (define (real-part z) (* (magnitude z) (cos (angle z)))) (define (imag-part z) (* (magnitude z) (sin (angle z)))) (define (magnitude z) (car z)) (define (angle z) (cdr z)) ;;; constructors (define (make-from-real-imag x y) (cons (sqrt (+ (square x) (square y))) (atan y x))) (define (make-from-mag-ang r a) (cons r a)) *** operators at the next higher layer ;;; client code (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)))) ** tagged data and explicit dispatch (2.4.2) Q: can we use both the polar and rectangular reps in the same program? No, what does make-from-mag-ang do? and which implementation of real-part is active? attached type tags to objects, then do a case analysis for each primitive --------------------------------------------------------- (define (real-part z) (cond ((rectangular? z) (real-part-rectangular (contents z))) ((polar? z) (real-part-polar (contents z))) (else (error "Unknown type -- REAL-PART" z)))) (define (rectangular? z) (eq? (type-tag z) 'rectangular)) (define (polar? z) (eq? (type-tag z) 'polar)) --------------------------------------------------------- Q: how did the type tags get in the objects? (define (make-from-real-imag-rectangular x y) (attach-tag 'rectangular (cons x y))) (define (make-from-mag-ang-rectangular r a) (attach-tag 'rectangular (cons (* r (cos a)) (* r (sin a))))) Q: What do the polar versions of these look like? Q: How would you implement attach-tag, type-tag, and contents ? Q: Why do we have to give the procedures funny long names? Q: What else needs to be done? define imag-part, magnitude, angle define make-from-real-imag, make-from-mag-ang (use each for its own) ** data-directed programming and additivity (2.4.3) checking the type of a data and calling an appropriate procedure is called "dispatching on type" *** problems - the generic interface procedures (real-part,...) must know about all the different representations - no two procedures in the entire system can have the same name, even though the individual representations can be designed separately i.e., the technique is not additive (modular) this is a big problem for very big systems *** data-directed programming --------------------------------------------------------- TABLE OF OPERATIONS Type Operation Polar | Rectangular ==========|==================|======================== real-part | real-part-polar | real-part-rectangular imag-part | imag-part-polar | imag-part-rectangular magnitude | magnitude-polar | magnitude-rectangular angle | angle-polar | angle-rectangular --------------------------------------------------------- This is what we have been doing so far the idea of data-directed programming is to use the table directly two operations on the table: (put op type proc) -- installs proc in the table, indexed by (op,type) (get op type) -- fetches the proc from table at index (op,type) a "package" interfaces procedures to the rest of the system by adding entries to the table --------------------------------------------------------- (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) (sqrt (+ (square (real-part z)) (square (imag-part z))))) (define (angle z) (atan (imag-part z) (real-part z))) (define (make-from-mag-ang r a) (cons (* r (cos a)) (* r (sin a)))) (define (tag x) (attach-tag 'rectangular x)) ;; interface to the rest of the system (put 'real-part '(rectangular) real-part) (put 'imag-part '(rectangular) imag-part) (put 'magnitude '(rectangular) magnitude) (put 'angle '(rectangular) angle) (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) --------------------------------------------------------- Q: What would Alyssa's polar "package" look like? Q: How would one call one of these procedures now? (define (apply-generic op . args) (let ((type-tags (map type-tag args))) (let ((proc (get op type-tags))) (if (procedure? proc) (apply proc (map contents args)) (error "No method for these types -- APPLY-GENERIC" (list op type-tags)))))) ;; 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)) Q: What about the constructors? have to get them from the table. ;; Constructors for complex numbers (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)) ** message passing (p. 186) *** in Scheme as in OOP, instead of having the operations do tests (row-based), and instead of using a table, decompose by columns, so that each object contains its own procs ;; 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)) Q: This seems to only work for methods with one argument, is that like anything in OOP? yes, the code found is based on one arg. *** in Java Q: How does this relate to Java?