411 REFERENCE · Wikibooks
Scheme Programming/Conditionals
Reference material by Wikibooks contributors. This retains its source identity and is not 411 news reporting.
Scheme Programming Using Variables Conditional Expressions Procedures
In this section we introduce Scheme's conditional expressions. These
forms allow us to make choices--something that's been lacking from the
simple programs we've seen so far.
Truth values in Scheme
We've already seen the boolean values #t ("true") and
#f ("false") in previous examples, as the values of
expressions like (< 3 5). Booleans have few procedures
R7RS § 6.3; the most commonly seen is probably not,
which negates its argument:
> (not #t)
#f
> (not #f)
#t
In Scheme, booleans are not the only objects with truth values; the
convention of Scheme is to consider #f to be false and
every other Scheme value to be true. A very common idiom in Scheme is
for procedures to return some useful value (which is rarely
#t) on success, and #f otherwise. While
our procedures will stick to the practice of returning #t,
it's important to be aware of this idiom.
and and or
The and and or formsWhile
and and or expressions look very much like
applications, these are not procedures but special forms. Scheme
has many special forms, each of which has its own syntax and semantics.
The conditional forms described in this section are some of the most
commonly-seen special forms. let us manipulate truth values in
familiar ways:
> (and #t #f)
#f
> (or #f (not #f))
#t
> (and (> 3 2) #t)
#t
> (or (<= 3 2) (zero? (- 10 3)))
#f
In this basic usage, these forms give us the Boolean AND and OR,
respectively, of their two arguments (called tests). This is often
perfectly sufficient. The and and or forms,
however, are a bit more flexible. For one thing, they can take any
number of tests:
> (and (> 3 2) (>= 5 4) (> 10 9))
#t
> (or (<= 2 3)
(zero? (- 10 3))
#f
(> 3 5))
#t
An and expression is thus true if all the tests
are true, and an or expression is true if at least one
of the tests is true.
As mentioned above, we can also have tests that
evaluate to things other than #t and #f:
> (and (> 3 2) 5)
5 ; ?
Since the first test expression evaluates to #t and the
second to 5 (which is not #f, the only Scheme value
considered false), this and expression must be true--but
why does it evaluate to 5? The Scheme convention is that
and, when all the tests are true, returns the value of
the last test; or returns the value of the first true test.
In this way, we can return values more useful than #t
without affecting the truth values of our expressions.
Simple Conditionals: if
The simplest conditional in Scheme is the if form.
Here are some examples:
> (if #t 1 0)
1
> (if (>= 5 8) 3 (+ 7 2))
9
Like and and or, if is a special form
with its own syntax. Here's the general form:
(if test consequent alternative)
The first component of an if
expression is a test expression, which is evaluated
first. If its value is true, the value of the second component
expression (the consequent) is returned. If the test evaluates to
false, on the other hand, we get the value of the third component
(the alternative).The third expression is optional. An
expression of the form (if b x), sometimes called a "one-armed
if", is legal Scheme. It gives the value of x if
b is true, and an unspecified value (which can be anything)
otherwise.
We'll see how this can be used in the "Advanced Scheme" chapter; for
now, we'll always include the alternative. Let's see how this works
in the above examples. In the first, the test expression is just
#t, so the value of the
if form is the value of the consequent expression,
1. To evaluate the second example, we first consider the
test, (>= 5 8). This evaluates to #f, so we
evaluate the alternative (+ 7 2) and return its value as
the value of the entire if expression.
Using if, we can write some useful procedures. A simple
example:
(define (absolute-value n)
(if (positive? n)
n
(- n))) ; with one argument, - gives the additive inverse
Using and and or forms in our test expressions,
we can combine several tests:
(define (days-in-year y)
(if (or (= (floor-remainder y 400) 0)
(and (= (floor-remainder y 4) 0)
(not (= (floor-remainder y 100) 0))))
366 ; leap year
365))
Rewrite days-in-year without and or
or; express the test using only if.
cond
We can write a great number of useful Scheme programs with
if. In fact, by chaining together ifs,
we can write any conditional expression we want. Unfortunately,
these expressions get complicated very quickly. When nesting
ifs, it can be difficult to keep track of all the
clauses and it may be necessary to write results several times.
(For example, in the last exercise, how many cases are there in
which 366 is the answer?) In these cases, we'd like
something more convenient.
For this reason, Scheme provides cond, an extremely
flexible conditional form which allows multi-way choices to be
expressed easily. Here's a short example:
(cond ((> 5 6) 7)
((= 3 (+ 1 2)) 4)
(else 8))
In its simplest form, cond takes a number of clauses,
each of which consists of a test and a result expression. To
evaluate a cond expression, we evaluate the tests of
each clause in turn; if one evaluates to true, cond
returns the value of that clause's result expression, skipping any
remaining clauses. A test which is just the word else
is always true, and thus a cond will always "choose"
the result of a clause with else as its test. Typically,
the last clause of a cond is an "else clause".
A simplified version of the form of a cond expression is:
(cond clause1 clause2 ...)
where each clause is of the form (test result) or
(else result).As we noted above, this is simplified.
A clause can actually take any number of result expressions,
giving us the general form
(test expression1 expression2 ...). If test is
true, each of these expressions is evaluated in turn, and the value
of the last is returned. With only one result expression, this is
equivalent to what we've described above.
cond also provides a notation called "cond arrow", which
we'll discuss in the Advanced Scheme chapter.
Let's step through the evaluation of a simple cond
expression.
(cond ((>= 5 8) 3)
(else (+ 7 2)))
To evaluate this expression, we look at the first clause,
((>= 5 8) 3). The test expression of this clause is
(>= 5 8), so we evaluate that; since its value is
#f, we skip this clause and go on to the next,
(else (+ 7 2)). The test of this clause is
else, which is "always true". We evaluate this clause's
result expression, (+ 7 2), and return its value,
9, as the value of the whole cond expression.
Since this cond expression returns 3 when
the first clause's test is true and 9 otherwise, it's
precisely equivalent to the following if form:
(if (>= 5 8) 3 (+ 7 2))
Thus, we can replace any if expression with an
equivalent cond. In fact, cond is so
general that we can use it in place of any other conditional form.
Notes