Tutorial notes #8 for 4/9 or 4/10/2007

TA:       Stephen McCamant
Email:    smcc@mit.edu
Location: 36-113A or 36-115
Project 3

I probably won't be able to give back
your projects before the next quiz, but
I'll send out a list of common issues by

Project 4

The object-oriented adventure game is
already posted. It should be fun, but
you'll notice it's also quite long.
There's a substantial design-your-own
part, for which you'll need to write up
a proposal a week before the final due

Louis's mutation problem

The following code attempts to count the
number of repeated elements in a list.
What's wrong with it?

(define (remove-dups! lst)
  (cond ((null? lst))
        ((null? (cdr lst)))
        ((= (car lst) (cadr lst))
         (set-cdr! lst (cddr lst))
         (remove-dups! lst))
         (remove-dups! (cdr lst)))))

(define (count-dups lst)
  (let ((old lst))
    (remove-dups! lst)
    (- (length old) (length lst))))
"count-dups" modifies its argument,
which is unexpected for a procedure
named "count". Adding a "!" on the end
would make this clearer, but better not
to modify in the first place. More
seriously, it always returns 0, since
"old" is just another reference to the
same list that gets modified by
remove-dups!, so it has its repeats
removed too.

How can we fix it?
Make a copy of the list before modifying

(define (copy-list lst)
  (append lst '()))

(define (count-dups lst)
  (let ((copy (copy-list lst)))
    (remove-dups! copy)
    (- (length lst) (length copy))))

Also useful, while we're on the subject:

(define (copy-tree tree)
  (if (pair? tree)
      (cons (copy-tree (car tree))
            (copy-tree (cdr tree)))

Environment model review

The environment model is a more
realistic model of Scheme evaluation
in which we keep track of the values of
variables using an "environment", a
chain of boxes called "frames" that hold
bindings of variable names to values.

New rules for the environment model:

variable: look up, starting in current
frame and going up to enclosing frames
until found

define: make new binding in current

set!: find binding as for lookup, and
replace value

lambda: create "double bubble": left
half points to parameters and body,
right to enclosing environment

To apply a compound procedure:
  * Evaluate each subexpression
  * Create new frame
  * New frame points to same parent as
    right half of double bubble
  * Bind parameter names to values
  * Evaluate body in new environment

Environment model exercises: basic

(define (infix a + b)
  (+ a b))
(infix 3 * 6)

|infix:-+       	       |
|*:-----+----------------------+--->#<prim mult><--+
|+:-----+----------------------+--->#<prim add>	   |
|	|       	       |    		   |
+-------+-^----------------^---+    		   |
 	| |     	   |        		   |
        V |            +---+---+    		   |
      ( )( ) 	       |a: 3   |		   |
       |   	       |+: ----+-------------------+
       > p: a + b      |b: 6   |
	 b: (+ a b)    +-------+

The result is 18.

(define pi 3.14159)
(define (circle-area r)
  (* pi r r))
(let ((pi 3))
  (circle-area 10))

+------------------------------+      +------+
|pi: 3.14159    	       |    E2|r: 10 |
|circle-area:-+                |<-----|      |
|       +-----+                |      |	     |
|	|                      |      +------+
 	| |                |
        V |            +---+---+
      ( )( )        E1 |pi: 3  |
       |               |       |
       > p: r          |       |
	 b: (* pi r r) +-------+

The result is 314.159.

A procedure with memory

I'd like to define a procedure named
running-sum that returns the sum of all
the numbers you've ever passed to it.
For instance:

(running-sum 10)
-> 10
(running-sum 10)
-> 20
(running-sum 5)
-> 25

How can	I write running-sum?
(define running-sum
  (let ((sum 0))
    (lambda (arg)
      (set! sum (+ sum arg))

Show how running-sum works using the
environment model

After the first call to running sum:

GE +--------------------------------+
   |running-sum:-+      	    |
   |		 |      	    |
   |		 |      	    |
   |	         |                  |
        	 | 	          |   	  E2
	         V 	   E1 +---+---+   +-------+
	      ( )( )--------->|sum: 10|<--+arg: 10|
	       |    	      |       |	  |       |
	       > p: arg	      +-------+	  +-------+
                 b: (set!...


A "box" is like a pair, except it holds
only one thing, rather than two. Like
pairs, we want to be able to mutate
boxes. We'll have three basic operation,
make-box, get-value, and set-value!:

(define a (make-box 1))
(define b (make-box 2))
(define c b)
(get-value c)
-> 2
(get-value a)
-> 1
(set-value! a 10)
(get-value a)
-> 10
(set-value! c 20)
(get-value b)
-> 20

It's easy to implement boxes using

(define (make-box v) (cons 'box v))
(define get-value cdr)
(define set-value! set-cdr!)

However, suppose we didn't have mutable
pairs. How could we create boxes using
just higher-order procedures and set!?
(define (make-box v)
  (lambda (msg arg)
    (cond ((eq? msg 'get)
          ((eq? msg 'set)
           (set! v arg)))))
(define (get-value box)
  (box 'get #f))
(define (set-value! box v)
  (box 'set v))

By the way, even though boxes may not
seem that useful, they are built into
DrScheme: look up "box" in the online
help if you're curious.

Environment model exercise: hair pulling

; Type: A -> (B -> A)
(define (k x)
  (lambda (y) x))

; Type:
; (A -> (B -> C)), (A -> B) -> (A -> C)
(define (s x y)
  (lambda (z)
    ((x z) (y z))))

((s k k) 42)
;-> 42