Lab 4a Solutions 1. Use a LET to keep both initial and current balance (define (make-account init-amount) (let ((BALANCE INIT-AMOUNT)) ;;; This is the change. (define (withdraw amount) (set! balance (- balance amount)) balance) (define (deposit amount) (set! balance (+ balance amount)) balance) (define (dispatch msg) (cond ((eq? msg 'withdraw) withdraw) ((eq? msg 'deposit) deposit))) dispatch)) 2. Add messages to read those variables. (define (make-account init-amount) (let ((balance init-amount)) (define (withdraw amount) (set! balance (- balance amount)) balance) (define (deposit amount) (set! balance (+ balance amount)) balance) (define (dispatch msg) (cond ((eq? msg 'withdraw) withdraw) ((eq? msg 'deposit) deposit) ((EQ? MSG 'BALANCE) BALANCE) ;; two lines added here ((EQ? MSG 'INIT-BALANCE) INIT-AMOUNT))) dispatch)) 3. Add transaction history. (define (make-account init-amount) (let ((balance init-amount) (TRANSACTIONS '())) ;; add local state var (define (withdraw amount) (SET! TRANSACTIONS (APPEND TRANSACTIONS (LIST (LIST 'WITHDRAW AMOUNT)))) ;; update (set! balance (- balance amount)) balance) (define (deposit amount) (SET! TRANSACTIONS (APPEND TRANSACTIONS (LIST (LIST 'DEPOSIT AMOUNT)))) ;; update (set! balance (+ balance amount)) balance) (define (dispatch msg) (cond ((eq? msg 'withdraw) withdraw) ((eq? msg 'deposit) deposit) ((eq? msg 'balance) balance) ((eq? msg 'init-balance) init-amount) ((EQ? MSG 'TRANSACTIONS) TRANSACTIONS))) ;; message to examine it dispatch)) 4. Why substitution doesn't work. (plus1 5) becomes (set! 5 (+ 5 1)) 5 The first line (the SET!) is syntactically wrong; "5" isn't a variable and it doesn't make sense to substitute into an unevaluated part of a special form. The second line (returning the value 5) is syntactically okay but gives the wrong answer; it ignores the fact that the SET! was supposed to change the result. 5. Draw environment diagrams when unsure. Lab 4B Solutions: 1. Environmental Diagram: the solution is on the course website under "Announcements" 3.12 append vs. append! exp1 is (b); exp2 is (b c d). Append (without the !) makes copies of the two pairs that are part of the list x. (You can tell because it uses cons, which is the constructor function that generates a brand new pair.) Append! does not invoke cons; it mutates the existing pairs to combine the two argument lists. 3. Set! vs. set-cdr! There are two ways to think about this, and you should understand both of them: The syntactic explanation -- SET! is a special form; its first argument must be a symbol, not a compound expression. So anything of the form (set! (...) ...) must be an error. The semantic explanation -- SET! and SET-CDR! are about two completely different purposes. SET! is about the bindings of variables in an environment. SET-CDR! is about pointers within pairs. SET! has nothing to do with pairs; SET-CDR! has nothing to do with variables. There is no situation in which they are interchangeable. (Note: The book says, correctly, that the two are *equivalent* in the sense that you can use one to implement the other. But what that means is that, for example, if we didn't have pairs in our language we could use the oop techniques we've learned, including local state variables bound in an environment, to simulate pairs. Conversely, we'll see in Chapter 4 that we can write a Scheme interpreter, including environments as an abstract data type, building them out of pairs. But given that we are using the regular Scheme's built-in pairs and built-in environments, those have nothing to do with each other.) 4a. Fill in the blanks. > (define list1 (list (list 'a) 'b)) list1 > (define list2 (list (list 'x) 'y)) list2 > (set-cdr! ____________ ______________) okay > (set-cdr! ____________ ______________) okay > list1 ((a x b) b) > list2 ((x b) y) The key point here is that if we're only allowed these two SET-CDR!s then we'd better modify list2 first, because the new value for list1 uses the sublist (x b) that we'll create for list2. So it's (set-cdr! (car list2) (cdr list1)) (set-cdr! (car list1) (car list2)) 4b. Now do (set-car! (cdr list1) (cadr list2)). Everything that used to be "b" is now "y" instead: > list1 ((a x y) y) > list2 ((x y) y) The reason is that there was only one appearance of the symbol B in the diagram, namely as the cadr of list1; every appearance of B in the printed representation of list1 or list2 came from pointers to the pair (cdr list1). The SET-CAR! only makes one change to one pair, but three different things point (directly or indirectly) to it.