Com S 342 meeting -*- Outline -*- * transforming procedural reps to data structure reps (3.6) ** motivation and overview corresponds to semantics, big picture for interpreters and compilers also ------------------------------------------ TRANSFORMING PROCEDURAL REPS TO DATA STRUCTURE REPS (3.6) Why? In semantics, often specify ADTs that are like functions: Idea: represent such an ADT by a _________ if need efficiency, transform to record How? represent the _______'s environment as a record, use the code from the _______ in the operation that "apply"s the record representation ------------------------------------------ ...environment = var -> location store = location -> value array = nat -> location proc = values * store -> value * store ... closure ------------------------------------------ PICTURE OF TRANSFORMATION (nnnnnnnnnnnnnnnnnn) ( ) ( ADT ) ( ) (uuuuuuuuuuuuuuuuuu) ^ ^ / \ / \ / \ !---------------! !-----------------! ! procedural rep! ! record rep ! ! ! **> ! ! ! ! ! ! !---------------! !-----------------! ------------------------------------------ use this to give an overview, explain that each will implement the abstraction the transformation is the arrow from the procedural rep box to the record rep box ** procedural reps (3.6.1) ------------------------------------------ EXAMPLE: INFINITE SEQUENCES seq = Nat -> number Constructors seq-repeat: (-> (number) seq) seq-generator: (-> ((-> (Nat) number)) seq) seq-add: (-> (seq seq) seq) Observers: seq-nth: (-> (seq Nat) number) Example: > (define ones (seq-repeat 1)) > (seq-nth ones 3) 1 > (define halves (seq-generator (lambda (n) (/ 1 (expt 2 n))))) > (seq-nth halves 0) 1 > (seq-nth halves 1) 0.5 > (seq-nth halves 2) 0.25 > (seq-nth halves 99) 1.577721810442e-30 > (seq-nth (seq-add ones halves) 2) 1.25 ------------------------------------------ Q: how would you represent seqs as closures? ------------------------------------------ INFINITE SEQUENCES AS PROCEDURES (define seq-repeat (lambda (num) (lambda (n) num))) (define seq-generator (lambda (f) (lambda (n) (f n)))) (define seq-nth (lambda (seq n) (seq n))) (define seq-add (lambda (s1 s2) ------------------------------------------ ... (lambda (n) (+ (seq-nth s1 n) (seq-nth s2 n))))) Write out the concrete types, and draw the correspondence to the abstract types. ** record representation (3.6.2) ------------------------------------------ TRANSFORMING TO RECORDS (1) How? 1. Constructors: a record type for each closure's environment (define seq-repeat ; procedural rep (lambda (num) (lambda (n) num))) (define seq-generator ; procedrual rep (lambda (f) (lambda (n) (f n)))) ;;; ... becomes ... (define-record repeated-seq ) (define-record generated-seq ) (define seq-repeat make-repeated-seq) (define seq-generator make-generated-seq) ------------------------------------------ ... (num) ... (f) Q: what are the free varrefs in the body of seq-repeat? seq-generator? ------------------------------------------ FOR YOU TO DO Define a record type that is the transform of the procedural rep for seq-add. (define seq-add (lambda (s1 s2) (lambda (n) (+ (s1 n) (s2 n))))) ------------------------------------------ (define-record added-seq (s1 s2)) (define seq-add make-added-seq) Q: Can you add them all up at the time you make one of these kinds of sequences? ------------------------------------------ TRANSFORMING TO RECORDS (2) How? 2. use the code from the closure in the operation that "apply"s the record representation (define seq-nth ; procedural version (lambda (seq n) (seq n))) (define seq-nth ; record version (lambda (seq n) (variant-case seq ( ------------------------------------------ Q: What are the cases? (repeated-seq (num) num) (generated-seq (f) (f n)) (added-seq (s1 s2) (+ (seq-nth s1 n) (seq-nth s2 n))) (else (error "invalid sequence" seq)))))) Q: will this still work with our examples? Yes, provided we go back and redefine ones and halves... Both can't coexist in the program (unlike OOP) ------------------------------------------ FINITE FUNCTIONS Idea behind environments, stores, ... (ff T): (-> (D) T), where D is a finite set of symbols Example: environments env: def: the *intension* of a function is def: the *extension* of a function is How could we represent a finite function? ------------------------------------------ ... (-> (symbol) T) or (-> (symbol) (cell T)) ... extensionally: as a Scheme function, intentensionally, as a list of two element lists: ((a 5) (x 25) (b 10) (y q)) or as a list of pairs: ((a . 5) (x . 25) (b . 10) (y . q)) Q: What kinds of operations would we want for this ADT? Q: How would those act on particular kinds of representations? ------------------------------------------ FINITE FUNCTIONS create-empty-ff: (-> () (ff T)) extend-ff: (-> (symbol T (ff T)) (ff T)) apply-ff: (-> ((ff T) symbol) T) ;; REQUIRES: symbol in the domain of ff BEHAVIOR (create-empty-ff) = {} (extend-ff s d f) = (apply-ff f s) = (apply-ff (create-empty-ff) s) is an error (apply-ff (extend-ff s d f) s2) = ------------------------------------------ ... {(s,d)} \union f ... if s \in dom(f), then f(s) else error ... (if (eq? s s2) d (apply-ff f s2)) Q: How would you represent these by procedures? ------------------------------------------ FOR YOU TO DO 1. Give record declarations for the constructors for "finite functions". (define create-empty-ff (lambda () (lambda (symbol) (error "empty-ff: no association " "for symbol" symbol)))) (define extend-ff (lambda (sym val ff) (lambda (symbol) (if (eq? symbol sym) val (apply-ff ff symbol))))) 2. Translate the following to the record representation. (define apply-ff (lambda (ff symbol) (ff symbol))) ------------------------------------------ See p. 93 for the answer ------------------------------------------ EXTENDING A FINITE FUNCTION WITH SEVERAL ASSOCIATIONS Why? binding formals to actuals declaring a record etc. ; PROCEDURAL REPRESENTATION (define extend-ff* ; p. 95 ;; TYPE: (-> ((list symbol) ;; (list T) ;; (ff T)) ;; (ff T) (lambda (sym-list val-list ff) (let ((val-vector (list->vector val-list))) (lambda (symbol) (let ((val (ribassoc symbol sym-list val-vector '*fail*))) (if (eq? val '*fail*) (apply-ff ff symbol) val)))))) ------------------------------------------ ribassoc is from exercise 2.2.7 Q: Why have the let outside the inner lambda? Isn't it used only once? saves recomputation each time the inner lambda is called... ------------------------------------------ RECORD REPRESENTATION (p. 95) (define-record extended-ff* (define extend-ff* (lambda (sym-list val-list ff) (make-extended-ff* sym-list (define apply-ff (lambda (ff symbol) (variant-case ff (empty-ff () (error "empty-ff: no association " "for symbol" symbol)) (extended-ff (sym val ff) (if (eq? symbol sym) val (apply-ff ff symbol))) (extended-ff* ------------------------------------------ ... (sym-list val-vector ff)) ... (list->vector val-list) ff))) ... (sym-list val-vector ff) (let ((val (ribassoc symbol sym-list val-vector '*fail*))) (if (eq? val '*fail*) (apply-ff ff symbol) val))) (else (error "apply-ff: Invalid finite function" ff))))) ** alternatives (3.6.3) ------------------------------------------ OPTIMIZATION ALTERNATIVES For convenience or efficiency consider other equivalents to records. Suppose only use extend-ff*. Records: #(extended-ff* sym-list4 val-vector4 #(extended-ff* sym-list3 val-vector3 ... #(extended-ff* sym-list1 val-vector1 #(empty-ff))) List of pairs (ribcage): ((sym-list4 . val-vector4) (sym-list3 . val-vector3) ... (sym-list1 . val-vector1)) ------------------------------------------ draw picture as in Figure 3.6.1 Q: which is more convenient? depends