Comp 210 Lab 11: State and a little I/O

set! examples, set! vs. set-structure-component!, I/O


set! examples

To change which thing a variable refers to, we use set!, as will be introduced in class. We'll preview set! by example.

To do as a group: Try evaluating the following examples. First try them in your head or on paper. Then try them in DrScheme to see if you were right.

To do:

  1. Develop

         ; factorial-count! : natnum -> natnum
         ; Purpose: Returns the factorial of its argument.
         ; Effects: Increments factorial-counter.
         ; Examples:
         ; #|
         ; > factorial-counter
         ; 0
         ; > (factorial-count! 3)
         ; 6
         ; > factorial-counter
         ; 1
         ; > (factorial-count! 5)
         ; 120
         ; > factorial-counter
         ; 2
         

    While this seems kind of silly, this idea comes up all the time. More generally, we can keep track of lots of kinds of information about how a function is used. A common reason is to profile the function, to see how often a function is used or what its arguments typically are, to know how to better optimize it.

    A solution.

  2. However, we would like to hide the counter factorial-count locally inside the function factorial-count!, so that nobody else can change it. Simply adding a local definition is easy enough, but there are two big problems if the counter isn't global:

    Develop

         factorial-count! : ('init or 'count or natnum) -> (void or natnum)
         
    The input is simply a "message" indicating one of the possible actions of the function: Part of the program just follows the three-case template for the above input type -- that should be easy. The "trick" is to define the counter in the right place in the definition. Every call to factorial-count! should use the same counter.

    Here's a standard solution. We'll see more examples like this in class.


set-structure-component!

An example

So let's make some sample structures:

     (define-struct person (birthyear haircolor annoying activity))

     (define mary-kate-olsen (make-person 1986 'blonde true 'smiling))
     (define ashley-olsen    (make-person 1986 'blonde true 'smiling))

     (define michelle-tanner1 mary-kate-olsen)
     (define michelle-tanner2 ashley-olsen)
You'll see why we're using the ultra-annoying Olsen twins as an example. (Heh, heh. Evil grin.) In case you don't know, they are the child stars who played Michelle Tanner in an inanely stupid TV show "Full House".

Clearly, your expectations are that

Q: What does the following do? First, step through the evaluation in your head, then confirm it with DrScheme.

     (define (kill-person! person)
        (begin
            (set-person-activity! person 'dead)
            (set-person-annoying! person false)))
     (kill-person! ashley-olsen)

     (person-activity michelle-tanner2) = ?
     (person-activity mary-kate-olsen) = ?

Note that, by convention, most Scheme functions and special forms which change the state of something are named with a bang (!). It helps to remind us that these things can be dangerous.

eq? tests whether two things are really the same thing, while equal? tests whether they look alike. Q:

     (eq? mary-kate-olsen michelle-tanner1)    = ?
     (eq? mary-kate-olsen ashley-olsen)        = ?
     (equal? mary-kate-olsen michelle-tanner1) = ?
     (equal? mary-kate-olsen ashley-olsen)     = ?

List examples

Cons-cells are just predefined kinds of structures, using a naming convention doesn't quite correspond to user-defined structures. They use the special forms set-car! and set-cdr!. (car and cdr are the traditional names for first and rest, respectively. However, the implementors of DrScheme forgot to define the names set-first! and set-rest!.) Remember that a list is not just a single structure, but a bunch of cons-cells.

To do:

     (define mylist (list 1 3 5 7))
     (define yourlist (cdr mylist))

     (set-car! yourlist 10)         ; the car/first should be a list element
     ; what is mylist now?
     ; what is yourlist now?

     (set-cdr! mylist (list 2 4))   ; the cdr/rest should be a list
     ; what is mylist now?
     ; what is yourlist now?

     (set-cdr! (cdr mylist) mylist)
     ; what is mylist now?
     ; what is yourlist now?

The shared output you'll see is basically the same as local.


Pragmantics: Using set! vs. set-structure-component!

We are going to get some practice using set! and set-structure-component!, as introduced in class. As a reminder,

When to use set! (Will be discussed in lecture.)

Remember that set! and set-structure-component! are as dangerous as they are useful. Careless side-effecting can have disasterous effects on your program. Use them wisely, little grasshoppper!


Input and output

Since Scheme automatically displays the result of whatever expression we give it, so far we haven't needed to know how to display something ourselves. Here are a few functions for getting information into and out of our programs, although Scheme has lots more. Try them out.

printf debugging

So far, you have used two good techniques for debugging code:

Using printf appropriately can provide one more good technique, by displaying important pieces of information. You briefly saw an example of this in class. The most common places to do this are at the beginning of a function, the end of a function, and after a bunch of set! or set-structure-component! assignments. With practice, you'll see that these and other techniques each have strengths and weaknesses.

The following version of factorial doesn't work:

     (define (buggy-factorial n)
         (cond [(zero? n) 1]
               [else      (* (sub1 n) (buggy-factorial (sub1 n)))]))

We will rewrite our code to print out the input(s) and output(s) of the function:

     (define debug? true)  ; do I want to display debugging info?

     (define (buggy-factorial n)
        (begin
           (if debug? (printf "buggy-factorial input: ~s~n" n))
           (local [(define result
                       (cond [(zero? n) 1]
                             [else      (* (sub1 n)
                                           (buggy-factorial (sub1 n)))]))]
               (begin
                  (if debug? (printf "buggy-factorial output: ~s~n" result))
                  result))))
To do: Call this function with some example inputs to see what the printed messages look like.

There are several things to note here: