CS 227 Lecture -*- Outline -*- * box-and-pointer representation of cons cells, mutation of cons cells (11.3) in this section we introduce the primitives set-car! and set-cdr! which mutate cons cells as a model of what is happening we use vectors and also draw pictures. ** implementing lists with vectors ------------------------ REPRESENTING LISTS WITH VECTORS (define cons (lambda (a d) (vector a d))) (define vector-accessor (lambda (i) (lambda (v) (vector-ref v i)))) (define car (vector-accessor 0)) (define cdr (vector-accessor 1)) ------------------------- really want tagged vectors, so can do pair? correctly, but ... ** box-and-pointer representation of cons cells --------------------- (DEFINE a (cons 3 '())) _____ _____ ____ a ----> | | --|--> |_()_| |__|__|_____| | | _v_ |_3_| --------------------- point out the parts: variable cell, cons cell, car, cdr boxes around objects (memory cells). a memory cell can hold an address of an object or a value. read an arrow as "is bound to" for variables, "means" or "denotes" for expressions, and "contains" or "refers to" inside objects address of a cell being stored in a cell has the tail of the arrow (the pointer) There is a memory cell associated with each variable in reality, this is not shown because it is not an object. In C++ these variable cells are important, and so you have to look at the fuller picture like this -------------- ______ _____ _____ ____ a | --|---->| | ---|--->|_()_| |______| |__|__|_____| | | _v_ |_3_| --------------------- we make no distinction between () and other objects book uses / instead of pointer to () because () often implemented by null pointer --------------------- _____ _____ a ----> | | / | |__|__|_/___| | | _v_ |_3_| ---------------------- other values (such as 3) could be written without boxes this in a kind of "optimization", however... obscures the "object-oriented" flavor of the pictures --------------------------- _____ _____ a ----> | | / | |__3__|__/__| ---------------------------- *** what is an object? an object is a box (memory cell) containing some value or (references to) other objects don't have to explicitly think about the pointers, since implicit and everywhere can't manipluate them just speak of on object containing another and expression denoting an object ------------------- SOME EXPRESSION EVALUATIONS ___ (+ 2 1) -----> |_3_| ____ 'hi -----> |_hi_| ___________ (vector 2 3 4) ----> |_2_|_3_|_4_| ----------------- we suppress the pictures of evaluation of 2, 1, 3, 4... *** what does cons do? cons allocates a new "cons cell" (two cells) and puts (references to) its two arguments in the car and cdr ---------------- WHAT CONS DOES _____ _____ ____ (cons 3 '()) ----> | | --|--> |_()_| |__|__|_____| | | _v_ |_3_| _____ _____ ____ (cons 3 '()) ----> | | --|--> |_()_| |__|__|_____| | | _v_ |_3_| _____ _____ ___ (cons 3 4) ------> | | --|--> |_4_| |__|__|_____| | | _v_ |_3_| ------------------- Draw a figure for (cons 'x (cons 'y (cons 'z '()))) ------------------- MORE BOX-CAR DIAGRAMS (cons 'x (cons 'y (cons 'z '()))) YOU DRAW A FIGURE FOR: (cons (cons 'x (cons 'y '())) (cons 'z '())) --------------------------- have students do the second one *** what define and set! do -------------------- (define a (cons 3 '())) _____ _____ ____ a ---------------> | | --|--> |_()_| |__|__|_____| | | _v_ |_3_| (set! a (cons 4 (cons 5 '()))) ------------------- Draw the picture of the evaluation of the set! *** garbage At this point, the cons cell (cons 3 '()) is garbage. garbage objects are not reachable from program variables. non-garbage objects are reachable from program variables. ** mutation of cons cells *** in terms of vectors Some of the power of vectors is not reflected here... we could provide something like vector-set! ------------------------ MUTATORS FOR CONS CELLS REP BY VECTORS (define set-car! ; TYPE: (-> ((pair S T) S) void) (lambda (cons-cell obj) ; MODIFIES: cons-cell (vector-set! cons-cell 0 obj))) (define set-cdr! ; TYPE: (-> ((pair S T) T) void) (lambda (cons-cell obj) ; MODIFIES: cons-cell (vector-set! cons-cell 1 obj))) ------------------------ Could use the following, but I think that might be confusing at a critical point: (define vector-settor! (lambda (i) (lambda (v o) (vector-set! v i o)))) *** set-car! and set-cdr! Draw picture of: (define a (cons 1 (cons 2 '()))) (define b (cons (cons 3 '()) (cons 4 (cons 5 '())))) (define c (cons a (cdr b))) this is figure 11.16 Note how the objects are shared. This has been happening all along but we haven't had to worry about it, because we haven't changed the lists Now show what happens if we do (set-car! (cdr b) a) note that set-car! doesn't have to take a variable as an arg. note that now (eq? (car (cdr b)) a) is true now do (set-car! (cdr b) 4) this restores the original picture show (set-car! b a) now (equal? c b) but not (eq? c b) Draw picture for (define f (cons 1 '())) (set-cdr! f f) try this to show what the interpreter does with circular lists. many of the exercises concern circular lists ** append! we can use set-cdr! to write a more space and time efficient and dangereous version of append... --------------- ; - Program 11.22, pg. 368 - (define append! ;TYPE: (-> ((list T) (list T)) (list T)) (lambda (ls1 ls2) ; MODIFIES: ls1 ; EFFECT: modify ls1 to have the ; elements of ls1 followed by ls2's (if (pair? ls1) (begin (set-cdr! (last-pair ls1) ls2) ls1) ls2))) ; - Program 11.21, pg. 368 - (define last-pair ; TYPE: (-> ((nonempty-list T)) ; (pair T (list T))) (lambda (x) (if (pair? (cdr x)) (last-pair (cdr x)) x))) ----------- Draw (define c (list 1 2 3)) (define d (list 4 5 6)) (define u (cons c (cons d '()))) ------------------ (define c (list 1 2 3)) (define d (list 4 5 6)) (define u (cons c (cons d '()))) ------------------ Show what is printed by c d u and then ask them what it will look like when we execute ---------------- (define y (append! c d)) ---------------- Now show what is printed by c d u and y Q: Is it any different if we execute (define y (append! (cdr c) d))? Q: What if we use append instead of append!? *** copying a list ---------------- (define list-copy (flat-recur '() cons)) (define a (cons 2 (cons 3 '()))) (define b (list-copy a)) (define c a) (set-cdr! a '()) --------------- draw a picture showing this... This is useful to make a fresh copy of a list, to separate the two lists before mutation.