set! vs. set-structure-component!, I/O
We are going to get some practice using set! and set-structure-component!, as introduced in class. As a reminder,
(set! var expr) changes the value of the variable/placeholder var to be the result of evaluating the expression expr.
(set-structure-component! structure-expr expr), changes the value stored in the named component of a structure to be the result of evaluating expr. The first subexpression must evaluate to the specified kind of structure.
To do:
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. However, we'd like to hide the counter; if we do, how can we access the counter's value? We'll see examples like that in class.
Using the structure
; A posn is a ; (make-posn x y) ; where x,y are numbers. (define-struct posn (x y))develop the following two programs.
posn+! : posn posn -> void changes the fields of the first argument so that, after this call, the first posn will be the sum of the two arguments. E.g., after
(define posn1 (make-posn 3 8)) (define posn2 (make-posn 9 1)) (posn+! posn1 posn2)then posn1 is (make-posn 11 10).
foreach-posn+! : list-of-posn posn -> void, which adds the second argument to each of the posns in the list.
Using the structure
; A counter is a ; (make-counter name value) ; where name is a symbol, and value is anything. (define-struct counter (name value))develop the following two programs.
count! : symbol list-of-counter -> void, which increments the value of the counter named by the symbol. If the symbol isn't the name of any of the counters, ignore it.
foreach-count! : list-of-symbol list-of-counter -> void, which increments the values of the corresponding counters, for each of the symbols, ignoring any symbols that don't name any of the counters.
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. Try them out.
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, ~s~n" 'world)displays the same thing. The ~s indicates that the next argument's value should be printed in place of the ~s.
(printf "~s ~s ~s~n" (+ 1 1) (list 'a 'b) #t)displays three things separated by spaces and terminated by a newline. The three things displayed are the values of the remaining arguments.
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 about ~s~n" 'this-error)causes an error. It also displays an error message using the symbol provided (typically the current function name), the format string, and any other arguments. Like printf, it expects only as many other arguments as specified by the format string.
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.
(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, ~s.~n" name)
So far, you have used two good techniques for debugging code:
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) (if debug? (printf "buggy-factorial input: ~s~n" n)) (local [(define result (cond [(zero? n) 1] [else (* (sub1 n) (buggy-factorial (sub1 n)))]))] (if debug? (printf "buggy-factorial output: ~s~n" result)) result))To do: Use this code on some examples to see what the printed messages look like.
There are several things to note here:
You don't actually need begin in many places. There is an implicit begin in the body of a define, local, or lambda.
We use a flag debug? to indicate whether or not to print out debugging info. This makes turning on/off debugging very easy.
For larger programs, we can extend this idea in various ways. It would be better to hide debug?. It is often useful to use multiple debugging flags, e.g., to debug separate parts of the code independently. It is often useful to use numbers instead of booleans to display different amounts of debugging info.
The conditional we use is called if, which is just shorthand for a cond:
(if test-exp then-exp else-exp) = (cond [test-exp then-exp] [else else-exp]) (if test-exp then-exp) = (cond [test-exp then-exp])
In general, it is very bad style to not have an else case, but it can be useful when we are only interested in the side-effects of the conditionals, and not their result values.
It is important to label any debugging values you display so that you know what your output means.
It is very easy to add the printf to display the input. However, it is somewhat cumbersome to add the printf to display the output since we have to name the result to refer to it twice.