411 REFERENCE · Wikibooks
Scheme Programming/A taste of Scheme
Reference material by Wikibooks contributors. This retains its source identity and is not 411 news reporting.
Scheme Programming Using a Scheme interpreter A taste of Scheme Scheme Datatypes
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)
37
Notice that here we've used the expression (* 8 5) as an
operand of another expression! This is valid 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
The definition (define x 20) causes the interpreter to
define x to mean the value 20. When x
appears in code following the definition, it will be replaced by this
value. We can then use x in the definition of y.
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
parameter, 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 (square 5).
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)
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). -,
*, and / are also variadic.
for each occurrence of . We solve this problem using the
let form, which allows us to temporarily associate the name
s with this value.If you're familiar with programming
languages with "local variables", you can think of names bound by
let as much the same thing.
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 #t and #f values which Scheme
gives us back here? As you may have guessed, these are boolean
values representing "true" and "false", respectively.
Predicates like 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 even?,
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
Evaluating (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
the 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
the if form; otherwise, we get the third.
For now, we'll assume that saying something is "true or false" means it's
a boolean--that is, either #t or #f. In
practice, every Scheme object has a boolean value; namely, #f is
false and everything else is true.
> (if #t 1 0)
1
> (if (> 2 3) 1 0)
0
1 Why does evaluating ((+ 3 6)) cause Scheme to report the "application of a non-procedure"?