6.001 Recitation #10 – March 12, 2003

 

RI: Konrad Tollmar

www.ai.mit.edu/~konrad/6001

 

• Data mutation

 

Until now we have not been able to change a variable once it has been defined. We have been programming using the functional programming paradigm, if we call a function with the same arguments we get the same result every time. Once we are allowed to change variables, this is no longer the case, procedures can now have properties that change from call to call. These properties are called state. When a variable changes we say that the state of the environment it was in has changed.

 

special form: set! variable [expression]

If expression is specified, evaluates expression and stores the resulting value in the location to which variable is bound. If expression is omitted, variable is altered to be unassigned; a subsequent reference to such a variable is an error. In either case, the value of the set! expression is unspecified.

Variable must be bound either in some region enclosing the set! expression, or at the top level. However, variable is permitted to be unassigned when the set! form is entered.

procedure: set-car! pair object

Stores object in the car field of pair. The value returned by set-car! is unspecified.

procedure: set-cdr! pair object

Stores object in the cdr field of pair. The value returned by set-cdr! is unspecified.

 

1. Evaluate these expressions:

(define x 10)                               Value: "x --> 10"

(set! x 6)                                  Value: 10

(+ x 3)                                     Value: 9

x                                           Value: 6

(set! x (+ x 3))                            Value: 6

x                                           Value: 9

(define y (cons 1 (cons 2 nil)))            Value: "y --> (1 2)"

(define z '(a b))                           Value: "z --> (a b)"

(cons y z)                                  Value: ((1 2) a b)

(append y z)                                Value: (1 2 a b)

(set-car! y 6)                              Value: #[unspecified]

y                                           Value: (6 2)

(set-cdr! y nil)                            Value: #[unspecified]

y                                           Value: (6)

(set-cdr! y z)                              Value: #[unspecified]

y                                           Value: (6 a b)

(set-car! z 12)                             Value: #[unspecified]

y                                           Value: (6 12 b)

(eq? (cons 'a nil) (cons 'a nil))           Value: #f

(equal? (cons 'a nil) (cons 'a nil))        Value: #t

(define w (list 'a 'b 'c))                  Value: "w --> (a b c)"

(eq? w (cdr (cons 'b w)))                   Value: #t

(eq? (cdr w) (cdr w))                       Value: #t

 

2. Append!

 

Rewrite append to append! That do not make a copy of the first list and cons'd it onto the second. Use instead side effects to append two lists without creating a copy of one of them. (Hint: define first a procedure last-pair)

 

(define (append lst1 lst2)

  (if (null? lst1)

      lst2

      (cons (car lst1) (append (cdr lst1) lst2))))

 

(define (append lst1 lst2)

  (set-cdr! (last-pair lst1) lst2)

  lst1)

 

(define (last-pair lst)

  (if (null? (cdr lst))

      lst

      (last-pair (cdr lst))))

 

 

3. Remember values

 

 

How does this work?

 

(define count (list 0))

(define (counter)

   (set-car! count (+ (car count) 1))

   (car count))

 

 

(counter) ==> ?

(counter) ==> ?

(counter) ==> ?

 

(define counter

  (let ((count (list 0)))

    (lambda ()

      (set-car! count (+ (car count) 1))

      (car count)

      )

    )

)

 

 

Write a function called seen? that takes one argument and returns #t only if that function has been called before with that argument. Assume you have the function element? that takes an object and a list, and returns #t if the object is in the list.

 

(seen? 1) -> #f

(seen? ‘x) -> #f

(seen? 1) -> #t

 

(define (seen?

  (let ((store (list 'values)))

    (lambda (x)

      (if (element? x (cdr store))

#t

            (begin (set-cdr! store (cons x (cdr store))) #f)))))

 

(define (element? x lst)

  (cond ((null? lst) false)

            ((equal? (car lst) x) true)

            (else (element? x (cdr lst)))))

 

 

 

 

 

4. Write make-ring

 

 

 

 

 

(define (make-ring! ring-list)

  (set-cdr! (last-pair ring-list) ring-list)

  ring-list)

 

(define (last-pair lst)

    (if (null? (cdr lst))

                      lst

                      (last-pair (cdr lst))))

 

 

(define ring (make-ring! (list 1 2 3 4)))

 

(define ring-l (rotate-left ring)

(nth 0 ring-l) ==>  2

 

(define (rotate-left ring) (cdr ring)  )

 

(define (ring-length ring)

   (define (helper n here)

      (if (eq? here ring)

          n

          (helper (+ 1 n) (cdr here))))

   (helper 1 (cdr ring)))

 

 

(define (rotate-right ring)

   (define (helper here)

      (if (eq? (cdr here) ring)

          here

          (helper (cdr here))))

   (helper  (cdr ring)))

 

 

 

5. Traditional LISP structure: association list

 

 

 (define (find-assoc key alist)

  (cond

   ((null? alist) #f)

   ((equal? key (caar alist)) (cadar alist))

   (else (find-assoc key (cdr alist)))))

 

 

(define a1 '((x 15) (y 20)))

(find-assoc 'y a1) ==> 20

 

(define (add-assoc key val alist)

   (cons (list key val) alist))

 

 

(define a2 (add-assoc 'z 10 a1))

 

 

a2 ==> ((z 10) (x 15) (y 20))

 

(find-assoc 'z a2) ==> 10

 

But - Alists are not an abstract data type

. It missing a constructor: Use quote or list to construct. So there is no abstraction barrier. Therefore, the implementation is exposed.

 

(define a1 ‘((x 15) (y 20)))

 

Lets instead use an alternative Table: a set of bindings, a pairing of a key and a value

Abstract interface to a table:

make
                                            create a new table

put! key value
                                            insert a new binding
                                            replaces any previous binding of that key

get key                                
                                            look up the key, return the corresponding value

 

This definition IS the table abstract data type

(define table1-tag .. )

 

(define (make-table1) .. )

 

(define (table1-get tbl key) .. )

 

(define (table1-put! tbl key val) .. )

 

 

 

 

 

 

 

(define table1-tag 'table1)

 

(define (make-table1) (cons table1-tag nil))

 

(define (table1-get tbl key)

  (find-assoc key (cdr tbl)))

 

(define (table1-put! tbl key val)

  (set-cdr! tbl (add-assoc key val (cdr tbl))))