Scheme Programming/A taste of Scheme
Now that we have an idea of how to run a Scheme interpreter, let's see what we can do with it in some short examples. We won't consider these programs in any depth. For now, the intention is to give the reader a feel for Scheme.
A Scheme interpreter evaluates Scheme expressions typed by the user. With our interpreter running, we first evaluate a very simple expression.
> 4 4
When we enter '4' and hit Enter, we are asking our Scheme interpreter to evaluate the expression '4'. Unsurprisingly, 4 evaluates to 4.
Next, we try some basic arithmetic.
> (+ 3 6) 9 > (* 18 5) ; multiplication 90 > (- 4 7) -3
These examples illustrate some important things about Scheme syntax. First, the syntax is fully-parenthesized; the parentheses in the above expressions are required, and extra parentheses cannot be added without changing the meaning of the expression. For example, the following causes the interpreter to report an error:
> ((+ 3 6)) ;; Error: application of non-procedure 9
Secondly, we see that Scheme uses prefix notation. In each expression, the operator (+, *, -) comes before the operands (the numbers). This is unlike the usual ("infix") convention of mathematical notation, in which the operator is written between the operands. For example, the following isn't a valid Scheme expression:
> (8 * 5 - 3) ;; Error: application of non-procedure 5
It's easy to translate it into Scheme, however:
> (- (* 8 5) 3) 16
Notice that here we've used the expression
(* 8 5) as an
operand of another expression! This is legal Scheme, and we will see
such nested expressions everywhere.
Scheme allows us to name values for use in other expressions:
> (define x 20) > x 20 > (define y (+ x 4)) > (* y 2) 48
(define x 20) causes the interpreter to
x to mean the value
appears in code following the definition, it will be replaced by this
value. We can then use
x in the definition of
We can also define procedures. Like the built-in operators
-, etc., a procedure takes a certain number
of parameters (also known as arguments) and computes a value.
> (define (square x) (* x x)) > (square 5) 25
Here, we have defined
square to mean a procedure taking one
x, and returning the value of
(* x x).
The form of this definition is a little different from those we saw
earlier; here, the name being defined appears in parentheses, followed
by the names of the procedure's parameters. To use this newly-defined
procedure, we evaluate
Here's a more complex procedure which calculates the area of a triangle with sides , , and using Heron's formula:
> (define (heron a b c) (let ((s (/ (+ a b c) 2))) (sqrt (* s (- s a) (- s b) (- s c))))) > (heron 4 13 15) 24
In this procedure, we need to use the value , defined by
four times. The definition of
heron would be tedious to
write and hard to read if we were to use
(/ (+ a b c) 2)
for each occurrence of . We solve this problem using the
let form, which allows us to temporarily associate the name
s with this value.
We can compare values, or test them for certain properties.
> (= 4 (+ 3 1)) #t > (< 9 7) #f > (positive? 5) #t > (even? 5) #f
What are the
#f values which Scheme
gives us back here? As you may have guessed, these are boolean
values representing "true" and "false", respectively.
positive? are procedures which take
one argument and return a boolean value. It's a Scheme convention to
give predicates names ending with '?'; hence we have
negative?, and many others.
Procedures can be recursive. Here's a classic example.
> (define (factorial n) (if (= n 0) 1 (* n (factorial (- n 1))))) > (factorial 5) 120
(factorial 5) gives us the value of ,
that is, , which we can
express recursively as . More generally, we
say that factorial of is 1 if is zero, and
times the factorial of otherwise. This
is precisely what the definition of
factorial states in the
language of Scheme:
(define (factorial n) (if (= n 0) ; is the argument 0? 1 ; then the answer is 1 (* n (factorial (- n 1))))) ; otherwise, it's n * (n - 1)!
Recursion is a critical notion in Scheme (and in computer science in general) that we will discuss in greater depth in later sections.
Another new thing in the definition of
factorial is the use of
if form, which evaluates its first argument, then either its
second or third argument depending on whether the first was true or false.
If the first argument evaluates to true, then we get the second argument of
if form; otherwise, we get the third.
> (if #t 1 0) 1 > (if (> 2 3) 1 0) 0
- In previous examples we gave
+two arguments. Here, though, we've given it three. Is this correct? In general, Scheme will report an error if we give a procedure too few or too many arguments.
+, though, is a variadic procedure, which means that it can be passed any number of arguments. The expression
(+ a b c)is equivalent to
(+ (+ a b) c).
/are also variadic.
- If you're familiar with programming
languages with "local variables", you can think of names bound by
letas much the same thing.
- For now, we'll assume that saying something is "true or false" means it's
a boolean--that is, either
#f. In practice, every Scheme object has a boolean value; namely,
#fis false and everything else is true.