Title: Environment Model cont'
1Lecture 14
- Environment Model (cont.) - Mutation- Stacks
and Queues
2Example explaining 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) gt 1(ca) gt
2(define cb (make-counter 0))(cb) gt 1(ca)
gt 3(cb) gt 2
2
3(define ca (make-counter 0)) GE
n 0
environment pointerpoints to E1because the
lambdawas evaluated in E1
(lambda () (set! n ( n 1)) n) E1
3
4(ca) GE
gt 1
empty
(set! n ( n 1)) E2
n E2 gt 1
4
5(ca) GE
gt 2
empty
(set! n ( n 1)) E3
n E3 gt 2
5
6(define cb (make-counter 0)) GE
n 0
(lambda () (set! n ( n 1)) n) E4
6
7(cb) GE
gt 1
7
8Capturing state in local frames procedures
8
9Lessons 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
9
10Explaining Nested Definitions
- Nested definitions block structure
- (define (sqrt x)
- (define (good-enough? guess)
- (lt (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))
???? ????? - ????? 13
10
11(sqrt 2) GE
guess 1
sqrt-iter
guess 1
good-enou?
???? ????? - ????? 13
11
12message passing example
(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)
???? ????? - ????? 13
12
13(define a (cons 1 2)) GE
car
cdr
a
p xb(x car)
p xb(x cdr)
???? ????? - ????? 13
13
14(car a) GE
gt 1
car
cdr
a
E1
x 1 y 2
p xb(x car)
p xb(x cdr)
dispatch
(x car) E2
???? ????? - ????? 13
(cond ..) E3 gt 1
14
15Mutating 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 pai
- 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!
set-car! and set-cdr! are procedures, while set!
is a special form
16Example Pair/List Mutation
- (define a (list red blue))
(red blue)
a gt
(define b a)
b gt
(red blue)
(set-car! a 10)
a gt
(10 blue)
b gt
(10 blue)
17Compare this with
- (define c (list red blue))
- (define d (list red blue))
c gt
(red blue)
d gt
(red blue)
(set-car! c 10)
(10 blue)
c gt
d gt
(red blue)
18Equivalent vs. The Same
In the first example a and b are the same. A
change to one changes the other. The point to a
single object.
In the second example c and d have at first the
same value, but later on one changes and the
other does not.They just happen to have at some
point in time the same value.
Without mutation, there is no identity equality
of value is all we have
19Example 2 Pair/List Mutation
(set-car! (cdr x) (list 1 2))
- Eval (cdr x) to get a pair object
- Change car pointer of that pair object
X
(a ( 1 2))
20Can create cyclic structures
(cddr x) gt ()
(set-cdr! (cdr x) x)
(caddr x) gt 1
- Beware of infinite scanning (or printing).
- Dr. Scheme prevents the infinite printing of
returned values (sometimes). Prints some
indication - x gt 0(1 2 . 0)
21Eq? vs. Equal?
- To check whether two names point to the same
object - Test with eq?
- (eq? a b) gt t
- To check whether two elements currently have the
same content - Test with equal?
- (equal? (list 1 2) (list 1 2)) gt t(eq?
(list 1 2) (list 1 2)) gt f
22Lets go over the following
- x gt (3 4)
- y gt (1 2)
-
- (set-car! x y)
- x gt
- (set-cdr! y (cdr x))
- x gt
- (set-cdr! x (list 7)
- x gt
((1 2) 4)
((1 4) 4)
((1 4) 7)
23We can actually get away only with set!
- (define (cons x y)
- (define (change-car val) (set! x val))
- (define (change-cdr val) (set! y val))
- (lambda (m)
- (cond
- ((eq? m 'car) x)
- ((eq? m 'cdr) y)
- ((eq? m 'set-car!) change-car)
- ((eq? m 'set-cdr!) change-cdr)
- (else
- (error "Undefined operation m)))))
24We can actually get away only with set!
- (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)
- Saw that pairs can be implemented with functions.
- Now we see that pair mutation can be implemented
with functions and set! - But set! is an essential addition.
25Another mutation example count-pairs
We want to count the number of pairs in a
list-structure
(define (count-pairs p) (if (not (pair? p))
0 ______________________________
____ __________________________________)
)
( (count-pairs (car p)) (count-pairs (cdr
p)) 1 )
26Mutation example count-pairs (contd)
gt (count-pairs (1 (2 3) 4)) 5 gt (count-pairs
(cons 1 (cons 2 3)) 2 gt (define l (1 2 3)) gt
(set-cdr! (cdr (cdr l)) l)
gt (count-pairs l) Time to drink a LONG espresso
27count-pairs improved version
(define (count-pairs1 p) (let ((pairs-list
null)) (define (cp-helper x) (cond
((not (pair? x) __________)
(___________________ 0) (else
______________________________
_______________________
_______________________)))
(cp-helper p)))
0
(memq x pairs-list)
(set! pairs-list (cons x pairs-list))
( (cp-helper (car x)) (cp-helper (cdr x))
1)
28Stack Data Abstraction
Last in, First out.
Insert
Delete
29Stack Data Abstraction
- constructor (make-stack) returns an empty
stack - selectors (top stack) returns current top
element from a stack - operations (insert stack elem) returns a new
stack with the element added to the top of
the stack (push) - (delete stack) returns a new stack with the
top element removed from the stack (pop) - (empty-stack? stack) returns t if no elements,
f otherwise - contract
- (delete stack) require (not (empty-stack? stack))
- (top stack) require (not (empty-stack? stack))
- ensure (equal? (delete (insert stack elem))
stack) - ensure (equal? (top (insert stack elem)) elem)
30Stack Implementation Strategy
- implement a stack as a list
d
b
a
- we will insert and delete items at the front of
the stack
31Stack Implementation
- (define (make-stack) nil)
- (define (empty-stack? stack) (null? stack))
- (define (insert stack elem) (cons elem stack))
- (define (delete stack)
- (if (empty-stack? stack)
- (error "stack underflow delete")
- (cdr stack)))
- (define (top stack)
- (if (empty-stack? stack)
- (error "stack underflow top")
- (car stack)))
32Limitations in our Stack
- Stack does not have identity
- (define s (make-stack))
- s gt ()
- (insert s 'a) gt (a)
- s gt ()
- (set! s (insert s 'b))
- s gt (b)
33Mutable Stack Implementation
The stack will be a mutable data type.
- The data type contains a list of elements as
before. - Insert and delete mutate a stack object.
- The first element of the list is special to
distinguish - Stack objects from other lists defensive
programming
34Mutable Stack Data Abstraction
- constructor (make-stack) returns an empty
stack - queries (selectors) (top stack) returns
current top element from a stack (empty-stack?
stack) returns t if no elements, f otherwise - (stack? any) returns t if any is a
stack, f otherwise - command (mutators, transformers) (insert!
stack elem) modify the stack by adding
elem to the top of the stack (push) - (delete! stack) modify the stack by removing
the top element from the stack (pop)
stack? is really not part of the stack abstraction
35Mutable Stack Implementation (1)
- (define (make-stack) (cons 'stack nil))
(define (stack? stack) (and (pair? stack) (eq?
'stack (car stack))))
(define (empty-stack? stack) (null? (cdr
stack)))
(define (top stack) (if (empty-stack? stack)
(error "stack underflow top") (cadr
stack)))
36Mutable Stack Implementation (2)
- (define (insert! stack elem)
- (set-cdr! stack (cons elem (cdr stack)))
- stack))
- (define (delete! stack)
- (if (empty-stack? stack)
- (error "stack underflow delete")
- (set-cdr! stack (cddr stack)))
- stack)
Here the mutators return the stack. May choose to
return a neutral value, eg. ok or nothing (more
later)
37Mutatable vs. functional
- The decision between a mutable stack and a
functional stack is part of the contract - Changing this changes the abstraction!
- The user should know if the object mutates or not
in order to use the abstraction correctly - For example, if we write
- (define stack1 stack2)
- can stack1 later change because of changes to
stack2 ?
38A Queue
FIFO First In, First Out
39A Queue
Insert
Insert
FIFO First In, First Out
Insert
x3
x2
Delete
40A Queue Implementation
- A queue is a list of queue elements
- The front of the queue is the first element in
the list - To insert an element at the tail of the queue,
need to scan the entire list, then attach the new
element at the rear
d
new
c
b
41A Queue Implementation (Cont)
- (define (make-queue) null)
- (define (empty-queue? q) (null? q))
- (define (front-queue q)
- (if (empty-queue? q)
- (error "front of empty queue" q)
- (car q)))
- (define (delete-queue q)
- (if (empty-queue? q)
- (error "delete of empty queue" q)
- (cdr q)))
- (define (insert-queue q elt)
- (if (empty-queue? q)
- (cons elt nil)
- (cons (car q) (insert-queue (cdr q) elt))))
42Complexity of the implementation
- For a queue of length n
- Time required -- number of cons, car, cdr calls?
- Space required -- number of new cons cells?
-
- front-queue, delete-queue
- Time T(n) T(1) that is, constant in time
- Space S(n) T(1) that is, constant in space
- insert-queue
- Time T(n) T (n) that is, linear in time
- Space S(n) T(n) that is, linear in space
43A more efficient implementation
- In order to make both insert and delete efficient
- we need constant time access to the front and the
rear of the queue. - we can achieve this if we keep a pointer to the
front and the rear of the queue. - A mutable implementation.
44Mutable Queue Data Abstraction
- constructor (make-queue) returns an empty
queue - queries (selectors, assessors)
- (front-queue q) returns the object at the
front of the queue. If queue is empty
signals error - (empty-queue? q) tests if the queue is empty
- commands (mutators, transformers)
(insert-queue! q elt) inserts the elt at the
rear of the queue and returns ok - (delete-queue! q) removes the elt at the front
of the queue and returns ok - additional query
- (queue? q) tests if the object is a queue
-
45Implementation
- We attach a type tag as before.
- Maintain queue identity
- Build a structure to hold
- a list of items in the queue
- a pointer to the front of the queue
- a pointer to the rear of the queue
46Queue Helper Procedures
- Hidden inside the abstraction
- (define (front-ptr q) (cadr q))
- (define (rear-ptr q) (cddr q))
- (define (set-front-ptr! q item)
- (set-car! (cdr q) item))
- (define (set-rear-ptr! q item)
- (set-cdr! (cdr q) item))
47Queue implementation
- (define (make-queue)
- (cons 'queue (cons null null)))
- (define (queue? q)
- (and (pair? q) (eq? 'queue (car q))))
- (define (empty-queue? q)
- (if (not (queue? q))
- (error "object not a queue" q)
- (null? (front-ptr q))))
- (define (front-queue q)
- (if (empty-queue? q)
- (error "front of empty queue" q)
- (car (front-ptr q))))
48Queue implementation Insert
- (define (insert-queue! q elt)
- (let ((new-pair (cons elt nil)))
- (cond ((empty-queue? q)
- (set-front-ptr! q new-pair)
- (set-rear-ptr! q new-pair)
- ok)
- (else
- (set-cdr! (rear-ptr q) new-pair)
- (set-rear-ptr! q new-pair)
- ok))))
49Queue implementation - delete
- (define (delete-queue! q)
- (cond ((empty-queue? q)
- (error "delete of empty queue" q))
- (else
- (set-front-ptr! q (cdr (front-ptr q)))
- ok)))
50Time and Space complexities ?
O(1)
51Programming Styles Procedural vs.
Object-Oriented
- Procedural programming - Organize system around
procedures that operate on data - (do-something ltdatagt ltarggt ...)
- (do-another-thing ltdatagt)
- Object-based programming - Organize system
around objects that receive messages - ((ltobjectgt 'do-something) ltarggt)
- ((ltobjectgt 'do-another-thing) ... )
- An object encapsulates data and operations
- Message passing and returned procedures are
the means to write object oriented code in scheme
52Stacks in OO style
(define (make-stack) (let ((top-ptr '()))
(define (empty?) (null? top-ptr)) (define
(delete!) (if (null? top-ptr)
(error . . .) (set! top-ptr (cdr
top-ptr))) top-ptr ) (define (insert!
elmt) (set! top-ptr (cons elmt top-ptr))
top-ptr) (define (top) (if (null?
top-ptr) (error . . .) (car
top-ptr))) (define (dispatch op) (cond
((eq? op 'empty?) empty?) ((eq? op 'top)
top) ((eq? op 'insert!) insert!)
((eq? op 'delete!) delete!))) dispatch))
53Stacks in OO style
(define s (make-stack)) ((s 'insert!) 'a)
gt ((s 'insert!) 'b) gt ((s 'top)) gt ((s
'delete!)) gt ((s 'top)) gt ((s 'delete!)) gt
(a)
(b a)
b
(a)
a
()
- compare with message passing examples in OO we
do not hide behind a functional layer eg. - (define (insert! s a) ((s insert!) a))
- In OO programming languages the notation is eg.
- s.insert(a)
- s.delete()