What if you want to define a procedure that computes values for even operands in a different way than for odd operands, or something tricky like that?
Actually, it's not that unusual -- lots of common mathematical functions are ``defined by cases.'' The absolute-value function, for instance: |x| = -x if x < 0; otherwise, |x| = x.
Right, that's a good example: How would you define abs, if
it weren't already built in?
You'd need a kind of expression we haven't seen before: an if-expression. The idea of an if-expression is that you want to combine three parts: (1) a test, which is simply an expression with a Boolean value; (2) a consequent, which is the expression to be evaluated in case the test expression is true; and (3) an alternate, which is the expression to be evaluated in case the test expression is false. The test selects which of the other two expressions should actually be evaluated; the expression that is not selected is simply skipped.
In the case of the absolute-value function, the test would be
(negative? x), the consequent would be (- x), and
the alternate would be simply x.
So how do these parts fit together into one expression?
An if-expression consists of a left parenthesis, the keyword
if, then the test, then the consequent, then the alternate,
and finally a right parenthesis -- for instance:
(if (negative? x)
(- x)
x)
What's the overall value of an expression like this? Does it have a
value? Yes. The value of the if-expression is the same as the value of the consequent or of the alternate, whichever is selected. (The value of the test expression is discarded after it is used to decide between the consequent and the alternate.)
And you could use an if-expression like this inside the body of a procedure definition?
It could be the body of a procedure definition:
(define (abs x)
(if (negative? x)
(- x)
x))
The procedure thus defined gives exactly the same results as the built-in
abs procedure.
So if is a procedure of arity 3, right?
No, if isn't a procedure at all, and an if-expression is not a
procedure call. In a procedure call, all the argument expressions are
evaluated before the procedure even begins to work, because the operand
values must be known in advance. But the whole point of an if-expression
is that you don't want to evaluate all of the constituent
expressions -- just the test expression and whichever of the other two it
selects.
Why couldn't you just evaluate the consequent and the alternate, and discard whichever one isn't selected?
Well, apart from the fact that it would lead to a lot of wasted effort, there are times when you would prefer not to evaluate some expression -- if, for instance, you can determine that it will crash.
Why would a programmer write an expression that she knows will crash?
The determination is done by the program as it is running, not by the
programmer as she writes the code. For instance, suppose you want to
divide one number, alpha, by another, beta,
except that you want to compute the value zero if beta happens
to be zero. You can write
(if (zero? beta)
0
(quotient alpha beta))
to get what you want. But if this were a procedure call, so that you had
to evaluate all the component expressions first, you'd be in real trouble
if beta happened to be zero. Evaluating (quotient alpha
beta) would crash your program before the procedure ever got around
to discarding the result.
How so? If beta were zero, the value of the alternate
expression wouldn't matter anyway.
Let me give you a demonstration. Now that we have if-expressions, we can
actually define a procedure that works the way you think if
should work:
(define (pseudo-if test consequent alternate)
(if test
consequent
alternate))
This pseudo-if really is a procedure, and when you
call it, all of the arguments will be evaluated first. Watch what happens:
> (define alpha 5)
> (define beta 0)
> (pseudo-if (zero? beta)
0
(quotient alpha beta))
Error: integer division by zero
(5)
0
The problem is that you're committed to performing the division before the
test can step in to intercept it, and it's the division that causes the
crash. And that's why if is not a procedure.
Next topic
Previous topic
Table of contents
This document is available on the World Wide Web as
http://www.math.grin.edu/~stone/scheme-web/if.html