Index:
set!
examples,
set-struct-field!
examples,
set!
vs.
set-struct-field!
I/O
Work through the many small exercises on your own, asking labbies at your leisure!
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 x 3) (begin (set! x (add1 x)) (set! x (add1 x)) x)evaluates each of its (three) expressions, throws away the results of all but the last, and returns the value of the last expression. In this case, -1 and 5, respectively. Unless the prior expressions mutate state, sequencing is not useful.
Syntax:
(begin expression ... expression)There must be at least one expression.
Semantics: Evaluate each expression in order, and return the value of that last one.
set!
examples
To change which thing a variable refers to, we use set!
, as
introduced in class.
For each, first try it in your head or on paper, then check your answer using DrScheme. These are "toy" examples that are not necessarily typical of real programs. However, they illustrate important issues of using state.
|
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. Here are a few functions for getting information into and out of our programs, although Scheme has lots more. 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.