#lang racket ;;;;;;;;;; ;; 2.73 ;; ;;;;;;;;;; (define deriv-table (make-hash)) (define (put type item) (hash-set! deriv-table type item)) (define (get type) (hash-ref deriv-table type)) (define (=number? x y) (and (number? x) (number? y) (= x y))) (define (variable? x) (symbol? x)) (define (same-variable? v1 v2) (and (variable? v1) (variable? v2) (eq? v1 v2))) (define (operator expr) (first expr)) (define (operands expr) (rest expr)) (define (deriv expr var) (cond [(number? expr) 0] [(variable? expr) (if (same-variable? expr var) 1 0)] [else ((get (operator expr)) (operands expr) var)])) ;; a. number? and same-variable? cannot be put in the op table because they do ;; not operate on tagged expressions. ;; constructors and selectors (define (make-sum a1 a2) (cond [(=number? a1 0) a2] [(=number? a2 0) a1] [(and (number? a1) (number? a2)) (+ a1 a2)] [else (list '+ a1 a2)])) (define (addend s) (first s)) (define (augend s) (second s)) (define (make-product m1 m2) (cond [(or (=number? m1 0) (=number? m2 0)) 0] [(=number? m1 1) m2] [(=number? m2 1) m1] [(and (number? m1) (number? m2)) (* m1 m2)] [else (list '* m1 m2)])) (define (multiplier p) (first p)) (define (multiplicand p) (second p)) (define (make-exponentiation base expo) (cond [(=number? expo 0) 1] [(=number? expo 1) base] [(and (number? base) (number? expo)) (expt base expo)] [else (list '** base expo)])) (define (base e) (first e)) (define (exponent e) (second e)) (define (install-differentiation-rules) (define (sum-rule expr var) (make-sum (deriv (addend expr) var) (deriv (augend expr) var))) (define (product-rule expr var) (make-sum (make-product (deriv (multiplier expr) var) (multiplicand expr)) (make-product (multiplier expr) (deriv (multiplicand expr) var)))) (define (power-rule expr var) (make-product (exponent expr) (make-product (make-exponentiation (base expr) (sub1 (exponent expr))) (deriv (base expr) var)))) (put '+ sum-rule) (put '* product-rule) (put '** power-rule)) (install-differentiation-rules) (deriv '(+ (* x y) (** x 3)) 'x) ;; '(+ y (* 3 (** x 2))) (deriv '(+ (* x y) (** x 3)) 'y) ;; 'x ;; d. If we indexed procedures by type and operator instead of by operator and ;; type we would only have to change put and get by interchanging the ;; parameters. Everything else would work the same. ;;;;;;;;;; ;; 2.74 ;; ;;;;;;;;;; ;; We need to create a proc table indexed by operation and type. The divisions ;; are the types, and the proc table would yield the correct procedure for that ;; operation that worked on that division's files. (define (get-record division name) ((get 'record division) name)) (define (get-salary division name) ((get 'salary division) name)) (define (find-employee-record name divisions) (cond [(null? divisions) (error "name not found -- FIND-EMPLOYEE-RECORD" name)] [else (define record (get-record (first divisions) name)) (or record (find-employee-record name (rest divisions)))])) ;; When acquiring a new division, headquarters will have to add a new column of ;; procedures to the proc table. ;;;;;;;;;; ;; 2.75 ;; ;;;;;;;;;; (define (make-from-mag-ang r a) (define (dispatch op) (cond [(eq? op 'real-part) (* r (cos a))] [(eq? op 'imag-part) (* r (sin a))] [(eq? op 'magnitude) r] [(eq? op 'angle) a] [else (error "unknown op -- MAKE-FROM-MAG-ANG" op)])) dispatch) ;;;;;;;;;; ;; 2.76 ;; ;;;;;;;;;; ;; We want to write a program that performs different operations on a data object ;; represented in different ways. For each operation and each representation, ;; there will be a different procedure that implements that operation on that ;; representation. How do we organize all these procedures to make later ;; modifying our program as simple as possible? ;; There are three possibilities: ;; We can sort the procedures by operation, and have one definition for each ;; operation that contains all the procedures implementing that operation on all ;; the different representations. This is the direct dispatch method. The ;; operations are called generic operations because they operate on data ;; represented in different ways. Direct dispatch is a good method if you think ;; you'll be adding new operations but no new representations because then you ;; only need to add a new definition for each new operation without modifying the ;; definitions of the already implemented operations. ;; We could also sort the procedures by representation type, and then define a ;; dispatch function for each type that takes an operation as a parameter and ;; returns the correct procedure that implements that operation on that ;; representation type. This is the message-passing style. Message-passing is a ;; good method if you'll be adding new types but no new operations because then ;; you only need to add a new dispatch definition for each new representation ;; without modifying the dispatch functions of the other already implemented ;; types. ;; The third possibility is to sort the procedures by operation and type, and ;; store all the procedures in a two-dimensional table. This is data-driven ;; programming. Data-driven programming is a good choice whether you'll be adding ;; new operations or new types, but it's best if you'll be adding both.