1
Lecture 13: Assignment and the Environment Model (EM)
Sections 3.1 and 3.2
2
To apply a compound procedure P to arguments:
1. Create a new frame A
2. Make A into an environment E: A's enclosing environment pointer goes to the same frame as the environment pointer of P
3. In A, bind the parameters of P to the argument values
4. Evaluate the body of P with E as the current environment
You mustmemorize these
four steps
3
(square 4) | GE
x: 10GE
parameters: xbody: (* x x)
square:
A
E1 x: 4
(* x x) | E1
*: #[prim]
==> 16
* | E1 ==> #[prim] x | E1 ==> 4
square | GE ==> #[proc]
4
Example: inc-square
GE
p: xb: (* x x)
square:inc-square:
p: yb: (+ 1 (square y))
(define square (lambda (x) (* x x))) | GE(define inc-square (lambda (y) (+ 1 (square y))) | GE
5
Example cont'd: (inc-square 4) | GE
GE
p: xb: (* x x)
square:inc-square:
p: yb: (+ 1 (square y))
E1y: 4
(+ 1 (square y)) | E1
+ | E1 ==> #[prim]
inc-square | GE ==> #[compound-proc ...]
(square y) | E1
6
Example cont'd: (square y) | E1
E2x: 4
(* x x) | E2
* | E2 ==> #[prim] x | E2 ==> 4
GE
p: xb: (* x x)
square:inc-square:
p: yb: (+ 1 (square y))
E1y: 4
==> 16 (+ 1 16) ==> 17
y | E1 ==> 4square | E1 ==> #[compound]
7
Lessons from the inc-square example
• EM doesn't show the complete state of the interpreter• missing the stack of pending operations
• The GE contains all standard bindings (*, cons, etc)• omitted from EM drawings
• Useful to link environment pointer of each frame to the procedure that created it
8
Example: make-counter
• Counter: something which counts up from a number
(define make-counter (lambda (n) (lambda () (set! n (+ n 1)) n )))
(define ca (make-counter 0))(ca) ==> 1(ca) ==> 2(define cb (make-counter 0))(cb) ==> 1(ca) ==> 3(cb) ==> 2 ; ca and cb are independent
9
(define ca (make-counter 0)) | GE
GE
p: nb:(lambda () (set! n (+ n 1)) n)
make-counter:
E1n: 0
(lambda () (set! n (+ n 1)) n) | E1
p: b:(set! n (+ n 1)) n
ca:
environment pointerpoints to E1
because the lambdawas evaluated in E1
10
(ca) | GE
(set! n (+ n 1)) | E2
1
n | E2 ==> 1
E2empty
GE
p: nb:(lambda () (set! n (+ n 1)) n)
make-counter:
E1n: 0
p: b:(set! n (+ n 1)) n
ca:
==> 1
11
E3
(ca) | GE
(set! n (+ n 1)) | E3
GE
p: nb:(lambda () (set! n (+ n 1)) n)
make-counter:
E1n: 0
p: b:(set! n (+ n 1)) n
ca:
1
n | E3 ==> 2
empty
==> 2
2
12
(define cb (make-counter 0)) | GE
(lambda () (set! n (+ n 1)) n) | E4
n: 0E4
p: b:(set! n (+ n 1)) n
cb:GE
p: nb:(lambda () (set! n (+ n 1)) n)
make-counter:
E1n: 2
p: b:(set! n (+ n 1)) n
ca:
E3
13
(cb) | GE
1n: 0
E4
p: b:(set! n (+ n 1)) n
cb:GE
p: nb:(lambda () (set! n (+ n 1)) n)
make-counter:
E1n: 2
p: b:(set! n (+ n 1)) n
ca:
E2
==> 1
E5
14
Capturing state in local frames & procedures
n: 1E4
p: b:(set! n (+ n 1)) n
cb:GE
p: nb:(lambda () (set! n (+ n 1)) n)
make-counter:
E1n: 2
p: b:(set! n (+ n 1)) n
ca:
E2
15
Lessons from the make-counter example
• Environment diagrams get complicated very quickly• Rules are meant for the computer to follow,
not to help humans
• A lambda inside a procedure body captures theframe that was active when the lambda was evaluated
• this effect can be used to store local state
16
Lets do a message passing example together
(define (cons x y) (define (dispatch op) (cond ((eq? op 'car) x) ((eq? op 'cdr) y) (else (error "Unknown op -- CONS" op)))) dispatch)
(define (car x) (x 'car))
(define (cdr x) (x 'cdr))
(define a (cons 1 2))
(car a)
17
Another thing the substitution model did not explain well:
• Nested definitions : block structure
(define (sqrt x) (define (good-enough? guess) (< (abs (- (square guess) x)) 0.001)) (define (improve guess) (average guess (/ x guess))) (define (sqrt-iter guess) (if (good-enough? guess) guess (sqrt-iter (improve guess)))) (sqrt-iter 1.0))
18
(sqrt 2) | GE
GE
p: xb:(define good-enou ..) (define improve ..) (define sqrt-iter ..) (sqrt-iter 1.0)
sqrt:
E1 x: 2good-enough:
p: guessb:(< (abs ….)
improve:sqrt-iter:
guess: 1
sqrt-iterguess: 1
good-enou?
19
Mutators for compound data
20
Compound Data
• constructor:
(cons x y) creates a new pair p
• selectors:
(car p) returns car part of pair
(cdr p) returns cdr part of pair
• mutators:
(set-car! p new-x) changes car pointer in pair
(set-cdr! p new-y) changes cdr pointer in pair
; Pair,anytype -> undef -- side-effect only!
21
Example: Pair/List Mutation
(define a (list 1 2))
(define b a)
a ==> (1 2)
b ==> (1 2) 1 2b
a
(set-car! a 10)
b ==> (10 2)
10
X
22
Example 2: Pair/List Mutation
(define x (list 'a 'b))
a b
xX
21
(set-car! (cdr x) (list 1 2))
1. Eval (cdr x) to get a pair object
2. Change car pointer of that pair object
• How mutate to achieve the result at right?
23
Sharing, Equivalence and Identity
• How can we tell if two things are equivalent?
-- Well, what do you mean by "equivalent"?
1. The same object: test with eq?(eq? a b) ==> #t
2. Objects that "look" the same: test with equal?(equal? (list 1 2) (list 1 2)) ==> #t(eq? (list 1 2) (list 1 2)) ==> #f
• If we change an object, is it the same object? -- Yes, if we retain the same pointer to the object
• How tell if parts of an object is shared with another? -- If we mutate one, see if the other also changes
24
x ==> (3 4)
y ==> (1 2)
(set-car! x y)
x ==>
followed by
(set-cdr! y (cdr x))
x ==>
Your Turn
3 4
x
1 2
y
((1 2) 4)
X
((1 4) 4)
X
25
Summary
• Scheme provides built-in mutators•set! to change a binding•set-car! and set-cdr! to change a pair
• Mutation introduces substantial complexity• Unexpected side effects• Substitution model is no longer sufficient to explain
behavior
26
The Implementation of Pairs
(define (cons x y)
(let ((new (get-new-pair)))
(set-car! new x)
(set-cdr! new y)
new))
27
Mutation as Assignment (1)
(define (cons x y) (define (set-x! v) (set! x v)) (define (set-y! v) (set! y v))
(lambda (m) (cond ((eq? m 'car) x) ((eq? m 'cdr) y) ((eq? m 'set-car!) set-x!) ((eq? m 'set-cdr!) set-y!) (else (error "Undefined operation" m)))))
28
Mutation as Assignment (2)
(define (car z) (z 'car))
(define (cdr z) (z 'cdr))
(define (set-car! z new-value) ((z 'set-car!) new-value) z)
(define (set-cdr! z new-value) ((z 'set-cdr!) new-value) z)