- Read By
- Friday, Sep 28, 2018
- Summary
- Many programs need to make choices. In this reading, we
consider Scheme’s
*conditional expressions*, expressions that allow programs to behave differently in different situations.

When Scheme encounters a procedure call, it looks at all of
the subexpressions within the parentheses and evaluates each
one. Sometimes, however, the programmer wants Scheme to exercise
more discretion. Specifically, the programmer wants to select just one
subexpression for evaluation from two or more alternatives. In such cases,
one uses a *conditional expression*, an expression that checks whether
some condition is met and selects the subexpression to evaluate on the
basis of the outcome of that condition. (We will sometimes refer to these
conditions as “tests”. Such tests are very different from the unit tests
that we do with RackUnit. RackUnit tests test our code for correctness.
The tests (conditions) in conditionals are a question about the state of
input or the system that let us make a decision.)

For instance, suppose we want to explicitly classify a city as “North” if its latitude is at least 39.72 and “South” if its latitude is less than 39.72. To write a procedure that like this, we benefit from a mechanism that allows us to explicitly tell Scheme how to choose which expression to evaluate. Such mechanisms are the primary subject of this reading.

The simplest conditional expression in Scheme is an * if expression*. An
if expression typically has three components: a condition, a consequent,
and an alternative. It selects one or the other of these expressions,
depending on the outcome of the condition. The general form is

```
(if condition consequent alternative)
```

We will return to the particular details in a moment. For now, let us consider the conditional we might write for the procedure to make a component more extreme.

```
(if (>= latitude 39.72) ; If the latitude is at least 39.72
"North" ; Classify it as North
"South") ; Otherwise, classify it as S"South"
```

To turn this expression into a procedure, we need to add the `define`

keyword, a name (such as `categorize-city`

), a lambda expression,
and such. We also want to give appropriate documentation and a bit of
cleanup to the results.

Here, then, is the complete definition of the `categorize-city`

procedure:

```
;;; Procedure:
;;; categorize-city
;;; Parameters:
;;; latitude, a real number
;;; Purpose:
;;; Categorize a city based on its latitude.
;;; Produces:
;;; northsouth, a string
;;; Preconditions:
;;; [No additional]
;;; Postconditions:
;;; If the latitude places the city at or above an imaginary dividing
;;; line, northsouth is "North"
;;; If the latitude places the city below that an imaginary dividing line,
;;; northsouth is "South"
(define categorize-city
(lambda (latitude)
(if (>= latitude 39.72) ; If the latitude is at least 39.72
"North" ; Classify it as North
"South"))) ; Otherwise, classify it as S"South"
```

In an `if`

-expression of the form ```
(if condition consequent
alternative)
```

, the * condition* is always evaluated
first. If its value is

`#t`

(which means “yes” or “true”),
then the `consequent`

`alternative`

`#f`

, then the consequent is ignored
and the alternative is evaluated.Scheme accepts `if`

-expressions in which the value of the condition
is non-Boolean. However, all non-Boolean values are classified as
“truish” and cause the evaluation of the consequent.

In some versions of Scheme, it is also possible to write an if expression
without the alternative. Such an expression has the form ```
(if condition
consequent)
```

. In this case, the condition is still evaluated
first. If the condition holds (that is, has a value of `#t`

or anything other
than `#f`

), the consequent is evaluated and its value is returned. If
the condition fails to hold (that is, has value `#f`

), the if expression has
no value.

Scheme programmers tend to use the alternative-free if expression much less frequently than they use the traditional form. In general, your first inclination should be to provide both a consequent and an alternative when you write a conditional. Some Scheme programmers object to the alternative-free if expression enough that they discourage its use.

Newer versions of Scheme provide another kind of conditional, the `when`

expression, which provides an alternative to the alternative-free if
expression.

```
(when guard
body1
body2
...
bodyn)
```

When evaluating a `when`

expression, the Scheme interpreter first
evaluates the * guard*. If the guard holds, the interpreter evaluates
each

`body`

`when`

sparingly. Our most frequent use will be in the context of preconditions.
For example, we may only do a computation when a precondition is met,
or we may issue an error message when a precondition is not met.`cond`

When there are more than two choices, it is often more convenient
to set them out using a * cond expression*. Like

`if`

, `cond`

is
a keyword. (Recall that keywords differ from procedures in that the
order of evaluation of the parameters may differ.) The `cond`

keyword is
followed by zero or more lists-like expressions called `cond`

clauses```
(cond
[guard-0
consequent-0]
...
[guard-n
consequent-n]
[else
alternate])
```

The first expression within a `cond`

clause is a guard, similar to
the condition in an `if`

expression. When the value of such a guard is
found to be `#f`

, the subexpression that follows the guard is ignored
and Scheme proceeds to the guard at the beginning of the next `cond`

clause. But when a guard is evaluated and the value turns out to be true,
or even “truish” (that is, anything other than `#f`

), the consequent
for that guard is evaluated and its value is the value of the whole cond
expression. Only one guard/consequent clause is used: subsequent `cond`

clauses are completely ignored.

In other words, when Scheme encounters a `cond`

expression, it works its
way through the `cond`

clauses, evaluating the guard at the beginning of
each one, until it reaches a guard that *succeeds* (one that does not have
`#f`

as its value). It then makes a ninety-degree turn and evaluates
the consequent in the selected `cond`

clause, retaining the value of
the consequent.

If all of the guards in a `cond`

expression are found to be false,
the value of the `cond`

expression is unspecified (that is, it might
be anything!). To prevent the surprising results that can ensue when
one computes with unspecified values, good programmers customarily end
every `cond`

expression with a `cond`

clause in which the keyword `else`

appears in place of a guard. Scheme treats such a `cond`

clause as if it
had a guard that always succeeded. If it is reached, the subexpressions
following `else`

are evaluated, and the value of the last one is the
value of the whole `cond`

expression.

For example, here is a `cond`

expression that attempts to figure out
what the type of `datum`

is and gives back a symbol that represents
that type.

```
(define type-of
(lambda (datum)
(cond
[(number? datum)
'number]
[(string? datum)
'string]
[(symbol? datum)
'symbol]
[else
'some-other-type])))
```

The expression has four `cond`

clauses. In the first, the guard is
`(number? datum)`

. If `datum`

is a number, the expression produces
the symbol `'number`

. If not, we proceed on to the second `cond`

clause.
Its guard is `(string? datum)`

. If datum is a string, the expression
produces the symbol `'string`

and nothing else. As you might guess,
the third `cond`

clause checks if `datum`

is a symbol, and, if so,
produces the value `'symbol`

. Finally, if none of those cases hold,
the `else`

clause produces the value `'some-other-type`

.

Note that at most one clause is used, and it is the first one. Consider the following similar expression to the one above.

```
(define numeric-type
(lambda (num)
(cond
[(real? num)
'real]
[(exact? num)
'exact]
[(integer? num)
'integer]
[else
'something-else])))
```

Suppose the input is `5`

, which is an exact integer, and therefore also
a real. Which output will we get? Let’s see.

```
> (numeric-type 5)
'real
> (numeric-type 'a)
. . exact?: contract violation
expected: number?
given: 'a
> (numeric-type 3+4.0i)
'something-else
```

You’ll note that we have to supply a number because `exact?`

expects a
number and we run that guard in the second case. Can we get a result
of `integer`

? Probably not, because every integer is real. Can we get
a result of `exact`

? Probably. We just need an exact complex number.

```
> (numeric-type 3+4i)
'exact
```

Although we have written our conditionals with one consequent per guard (and you will often to do the same), it is, in fact, possible to have multiple consequents per guard.

```
(cond
[guard-0
consequent-0-0
consequent-0-1
...
consequent-0-m]
[guard-1
consequent-1-0
consequent-1-1
...
consequent-1-n]
...
[else
alternate-0
alternate-1
...
alternate-a])
```

In this case, when a guard succeeds, each of the remaining subexpressions
(that is, consequents) in the same `cond`

clause is evaluated in turn,
and the value of the last one becomes the value of the entire `cond`

expression.

As you may have noted from our discussion of `cond`

, `cond`

expressions
can use square brackets rather than parenthesis to indicate structure.
That is, they do not surround an expression to evaluate (a procedure
followed by its parameters). Instead, they serve only to group things. In
this case, the parentheses group the guard and consequents for each
`cond`

clause. The square brackets are just a notational convenience;
parenthesis will work just as well, and you’ll see a lot of Scheme code
that uses parentheses rather than square brackets. Racket, like most
modern Scheme implementations, allows both because the square brackets
add a bit of clarity.

When writing `cond`

clauses, you should take the time to verify that
you’ve used the right number of parentheses and square brackets. Each
clause has its own open and close square brackets (or open and close
parenthesis). Typically, the guard has parentheses, unless it’s the
`else`

clause. Make sure to include both sets.

Remember that DrRacket’s “reindent” feature (Ctrl-i) helps you see if you’ve matched your parenthesis correctly. If the indentation looks correct, the parentheses are likely correct. If the indentation does not look correct, you should have a clue about missing parentheses.

`and`

and `or`

As we saw in the reading on Boolean values, both
`and`

and `or`

provide a type of conditional behavior. In particular,
`and`

evaluates each argument in turn until it hits a value that is
`#f`

and then returns `#f`

(or returns the last value if none return
`#f`

). Similarly, `or`

evaluates each argument in turn until it finds
one that is not `#f`

, in which case it returns that value, or until it
runs out of values, in which case it returns `#f`

.

That is, `(or exp0 exp1 ... expn)`

behaves much like the
following `cond`

expression, except that the `or`

version evaluates each
expression once, rather than twice.

```
(cond
[exp0
exp0]
[exp1
exp1]
...
[expn
expn]
[else
#f])
```

Similarly, `(and exp0 exp1 ... expn)`

behaves much like
the following `cond`

expression.

```
(cond
[(not exp0)
#f]
[(not exp1)
#f]
...
[(not expn)
#f]
[else expn])
```

Most beginning programmers find the `cond`

versions much more
understandable, but some advanced Scheme programmers use the `and`

and `or`

forms because they find them clearer. Certainly, the `cond`

equivalents for both `or`

and `and`

are quite repetitious.

`(if condition consequent alternative)`

*Standard keyword.*- Evaluate
. If its value is truish (that is, anything but false), evaluate`condition`

and return its value. If the value of the condition is false (#f), evaluate and return`consequent`

.`alternative`

`(cond [guard-1 consequents-1] [guard-2 consequents-2] ... [guard-n consequents-n] [else alternative])`

*Standard keyword.*- Evaluate each guard in turn until one is truish. It then evaluates the corresponding sequence of consequent expressions and returns the value of the last consequent. If none of the guards is truish, evaluates the alternative and returns its value.
`(when guard exp1 exp2 ... expn)`

*Optional Scheme Keyword.*- Evaluate
. If it holds, evaluate each expression in turn. Otherwise, do nothing.`guard`

`(and exp1 exp2 ... expn)`

*Standard keyword.*- Evaluate each expression in turn. If any of those values is false, return false. Otherwise, return the value of the last expression.
`(or exp1 exp2 ... expn)`

*Standard keyword.*- Evaluate each expression in turn. If any of those values is truish, return the first truish value. Otherwise, return false.

a. Assuming `num`

is defined as an integer, write an `if`

expression
that produces double the value of `num`

if it is odd, and half the
value otherwise.

b. Write a `cond`

expression that produces the symbol `positive`

if `num`

is greater than zero, the symbol `negative`

if `num`

is less than zero,
and the symbol `neither`

otherwise.