define vs. set!, values vs. printing, printf, simple loops

September 22, 2009
by Lee Spector (lspector)

The following is a message I sent previously to the class email list, providing some information on a couple of aspects of Scheme that came up in class, but I’m posting it here for future reference.

First, variables are created with “define”, but if you want to give a new value to an existing variable you should use “set!”. This is particularly important if you’re giving it a new value within a procedure or some other complicated form, because “define” can only be used at the top level, not nested within complex expressions. So for example here I first define myvar but then use set! to give it a new value (which is made in part out of pieces of its old value):

> (define myvar '(some stuff))
> myvar
(some stuff)
> (set! myvar (cons (car myvar) (cons 'more (cdr myvar))))
> myvar
(some more stuff)
>

In that case I could actually have used define a second time too, even though it’s not the best style. But if I wanted to put it in a function then I’d have to use set!, like this:

> (define myvar '(some stuff))
> (define add-more
   (lambda ()
     (set! myvar (cons (car myvar) (cons 'more (cdr myvar))))))
> (add-more)
> myvar
(some more stuff)
>

But define wouldn’t work:

> (define add-more
   (lambda ()
     (define myvar (cons (car myvar) (cons 'more (cdr myvar))))))
. begin (possibly implicit): no expression after a sequence of internal definitions in: ((define myvar (cons (car myvar) (cons (quote more) (cdr myvar)))))
>

That’s an error message, albeit a totally confusing one (as error messages sometimes unavoidably are) — the full glory with the red stopsign doesn’t appear here, but you get the idea. So use set! if you want to give an existing variable a new value.

Next, I want to show you something about printing values. When we’ve been evaluating expressions in the REPL we’ve seen the values of the expressions printed by the “P” in REPL, which stands for Read Evaluate Print Loop. Something similar happens when you put a bunch of expressions in the definitions pane and hit Run — it prints the results of all of the expressions that it evaluates (and note that “define” expressions produce no values, so nothing gets printed for those).

If you want to print something that is not the value returned from a top-level expression you can use one of the several output functions like “print”, “display” or “printf”. Because printf is one of the most generally useful I want to show you a little about it real quick.

To print a string you can pass it to printf like this:

> (printf "Hey -- I'm a string!")
Hey -- I'm a string!
>

But maybe you can’t tell that this was printf printing it instead of the REPL… So here’s a procedure that takes two arguments, prints a string, and then returns the value of adding the arguments:

> (define add-with-a-gratuitous-message
   (lambda (x y)
     (printf "Gratuitous message printed by printf!")
     (+ x y)))
> (add-with-a-gratuitous-message 23 100)
Gratuitous message printed by printf!123
>

So you can see that printf printed the string and THEN the REPL printed the value returned by the procedure call, which is 123.

Now the fact that that the string and the number 123 got smushed together on one line is probably making you wonder how to put line breaks in a string. The answer is to put “\n” in the string (which is a convention from C) or alternatively “~%” (which is a convention from Common Lisp). So:

> (define add-with-a-gratuitous-message
   (lambda (x y)
     (printf "Gratuitous message\nprinted by printf!\n") ;; NOTE: there are two \n in this line
     (+ x y)))
> (add-with-a-gratuitous-message 23 100)
Gratuitous message
printed by printf!
123
>

If you want to print something other than a canned message you can include “~A” in the string and then include whatever value or value-producing expression after the string — the value will be substituted in place of the ~A. So for example:

> (define add-with-a-gratuitous-message
   (lambda (x y)
     (printf "I got ~A for x, ~A for y.\n" x y)
     (printf "And if you're curious their product is ~A.\n" (* x y))
     (+ x y)))
> (add-with-a-gratuitous-message 23 100)
I got 23 for x, 100 for y.
And if you're curious their product is 2300.
123
>

One more quick item: loops. There are actually many ways to make loops in Scheme, but for those of you who want to make simple numerical counting loops here’s a quick way to do it:

> (for ((i (in-range 5 10)))
   (printf "~A\n" i))
5
6
7
8
9
>

I’ll give more details as requested in class (or in email).

-Lee



Leave a Reply

You must be logged in to post a comment.