|
Comp210: Principles of Computing and Programming
|
Index:
set-struct-field!
revew,
begin
,
set-struct-field!
examples,
I/O
Work through the many small exercises on your own, asking labbies at your leisure!
set-struct-field!
reviewWhen we define a structure type, e.g.,
(define-struct foo (bar baz))we've previously seen that DrScheme defines a number of functions for us.
make-foo : (type of bar) (type of baz) -> foo foo? : any -> boolean foo-bar : foo -> (type of bar) foo-baz : foo -> (type of baz)In addition, it also defines two other functions (called mutators)
set-foo-bar! : foo (type of bar) -> void set-foo-baz! : foo (type of baz) -> voidRather than creating a new
foo
, they change
the given one. Changing an existing value is called a side-effect. By
convention, most Scheme functions with side-effects have names ending with a
"bang" (exclamation point).
This also introduces the type void
, which indicates no information.
begin
We can sequence operations with begin
. For example,
; An uninteresting use of begin. (begin (+ 2 3) (* 2 3) (- 2 3)) ; An interesting use of begin. (define-struct cat (name age high? buddy)) (define acat (make-cat 'furball 3 false '??dummy??)) (begin (set-cat-age! acat (add1 (cat-age acat))) (set-cat-high?! acat true) acat)evaluates each of its (three) expressions, throws away the results of all but the last, and returns the value of the last expression. In the second case, nothing is thrown away, because the mutators don't return anything. Sequencing is only useful when the prior expressions mutate state, as in the second case.
Syntax:
(begin expression ... expression)There must be at least one expression.
Semantics: Evaluate each expression in sequential order, and return the value of that last one.
set-struct-field! examples
In class we discussed how constructors (e.g., make-foo
) evaluate to
a reference to a new structure, and not to the new structure itself.
This allows us to share references to the same structure.
Also, we discussed how to mutate (change) structures. This lets our
program more closely match what we're modeling.
|
Cons-cells are just predefined kinds of structures, using a naming convention
that doesn't quite correspond to user-defined structures. For mutating these
structures, Scheme provides the functions
set-first!
and set-rest!
. (Remember that a list is not
just a single structure, but a bunch of cons-cells, or
empty
(which is a flat value -- not a (reference to a) structure!)
(define mylist (list 1 3 5 7)) (define yourlist (rest mylist)) (set-first! yourlist 10) ; The first should be a list element. ; what is mylist now? ; what is yourlist now? (set-rest! mylist (list 2 4)) ; The rest should be a list. ; what is mylist now? ; what is yourlist now? (set-rest! (rest mylist) mylist) ; what is mylist now? ; what is yourlist now? |
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 (aside from the drawing examples). Here are a few functions for getting information into and out of our programs. (Scheme has many others, too.) Try them out.
printf
printf
displays information to the screen. It takes at
least one argument: a string giving the format of what to display.
printf
may take more arguments, if so specified by the format.
E.g.,
(printf "hello, world~n")displays the string
hello, world
, followed by a newline. The
~n
means a newline.
(printf "hello, ~v~n" 'world)displays the same thing. The
~v
indicates that the next
argument's value should be printed in place of the ~v
. (Mnemonic:
"v" meaning any value.)
(printf "~v ~v ~v~n" (+ 1 1) (list 'a 'b) true)displays three things separated by spaces and terminated by a newline. The three things displayed are the values of the remaining arguments.
error
error
causes an error to happen and displays an error message.
In this class, we haven't been concerned with writing functions that
check for bad inputs. In real life and later classes, we'd want to check
and report any errors, including bad inputs.
(error 'myfunction "error message")causes an error. It also displays an error message using the symbol provided (typically the current function name), the string for the error message. The
format
function can be used to format the error string
before it is passed to
error
.
read
read
stops a program, waits for the user to type something in,
and returns the value that the user just typed in. It takes no
arguments. Each time it is used, it could return a different value,
since the user can type in something different each time.
Execute the following code, typing in answers when prompted.
(printf "I will read, but immediately forget the next thing you type.~n") (read) (printf "What is your name? ") (define name (read)) (printf "Howdy there, ~v.~n" name)Observe how words you type in are read as symbols. You can click on "eof" to type in an "end-of-file" signal. (Aside: you can use ~a
to format output suitable for non programmers -- that is, symbols aren't
preceded by
' and strings aren't surrounded by " s.)
|
So far, you have used two good techniques for debugging code:
Consider the following incorrect version of factorial.
;; buggy-fact: natnum --> number ;; Return n!. ;; OOPS -- doesn't work -- always returns 0?!! ;; (define (buggy-fact n) (cond [(zero? n) 1] [else (* (sub1 n) (buggy-fact (sub1 n)))]))
DrScheme has a version of tracing built-in that is very easy to use.
|
For the curious...
Some programming environments like DrScheme having tracing built-in. But, some
don't. So, it can be valuable to understand how to incorporate tracing into your
own program. The basic idea is simply to add printf
's to print out
the input(s) and output(s) of the function. (Thus the name "printf debugging",
since the function is also called
printf
in the language C, where this technique is common.)
(define debug? true) ; Do I want to display debugging info? ;; buggy-fact: natnum --> number ;; Return n!. ;; OOPS -- doesn't work -- always returns 0?!! ;; (define (buggy-fact n) (begin (when debug? (printf "buggy-fact input: ~v~n" n)) (local [(define result (cond [(zero? n) 1] [else (* (sub1 n) (buggy-fact (sub1 n)))]))] (begin (when debug? (printf "buggy-fact output: ~v~n" result)) result))))
Here, we used a boolean flag debug?
to indicate whether or
not to print out debugging info. This makes turning on/off debugging very easy.
It can get very annoying to add and remove debugging repeatedly.
Also, we used a conditional called when
:
(when test-exp exp ... exp)evaluates its
test-exp
, and if true it returns
(begin exp ... exp)
); if the
test-exp
is false, then no useful value is returned (just as
set-struct-field!
and
printf
don't return any useful value).
There are lots of variations on this idea to make it more useful. For example, we'd like to encapsulate the debugging aspect into a separate function. And we'd like finer control over what specific functions we want to trace.
Last Revised Thursday, 04-Nov-2004 00:49:40 CST
©2004 Stephen Wong and Dung Nguyen