Scheme control structures are expressions, and therefore return values
when evaluated.
An if
expression is a lot like a C if
statement, but the
"then" branch and the "else" branch are also expressions that return
values; the if
expression returns the value of whichever
subexpression it evaluates.
For example,
(if (< a b) a b)
returns the value of either the variable a
, or the variable b
,
whichever is less (or the value of b
if they're equal). If you're
familiar with
ternary(1) expressions in C, this
expression is like (a < b) ? a : b
. In Scheme, there's no need
for both an if
statement and an if-like ternary expression
operator, because if
"statements" are expressions.
Note that even though every expression returns a value, not
all values are used--you can ignore the return value of an
if expression. The if
special form can therefore
be used to control what gets executed, or to return a value, or
both. It's up to you.
The uniformity of value returning means that we never have
to explicitly use a return statement, so Scheme doesn't have one.
Suppose we wanted to write a function min
to return the
minimum of two numbers. In C, we might do it this way (although we
should probably use the conditional expression here):
int min(int a, int b) { if (a < b) return a; else return b; }
In Scheme, we can just do this:
(define (min a b) (if (< a b) a b))
Whichever branch is taken, the value of the appropriate variable (a
or b
) will be returned as the value of that branch of the if
,
which is returned as the value of the whole if
expression, and that
is returned as the return value of the procedure call.
Of course, you can also write a one-branch if, with no "else" clause.
(if (< a b) (display a))
The return value of a one-branch if
is unspecified in the case
the condition is false, so if you're interested in the return value,
you should use a two-branch if
, and explicitly specify
what should be returned in both cases.
Notice that the flow of control is top-down, through the nesting of
expressions---if
controls which of its subexpressions is
evaluated, which is like the nesting of control statements in
most languages. Values flow back up from expressions to their callers,
which is like the nesting of expressions in most languages.
You can write an expression that is an ordered sequence of other
expressions, using begin
. For example,
(begin (display a) (+ a b))
calls display
and then calls +
. In terms of control flow, a
(begin
... )
expression is rather like a
begin
... end
block in Pascal, or a
{
... }
block in C. (We don't need an end
keyword, because the closing parenthesis does the job.)
Scheme begin
expressions aren't just code blocks, though, because
they are expressions that return a value. A begin
returns the
value of the last expression in the sequence. For example, the begin
expression above returns the value returned by the call to +
(i.e., the sum of a and b.)
The bodies of procedures work like begin
s as well. If the
body contains several expressions, they are evaluated in order, and
the last value is returned as the value of the procedure call.
Here's a procedure baz
that calls display
and then calls
+
and returns the result from the call to +
.
(define (baz) (display a) (+ a b))