• No results found

The Meaning of Scheme

In document How to Design Programs (Page 118-123)

Stepper

A legal DrScheme program consists of two items: a sequence of function definitions (in the Definitions window) and a sequence of interactions (in the Interactions

window). Each interaction is a demand for the evaluation of one Scheme expression, which typically refers to the functions defined in the upper part of DrScheme.

When DrScheme evaluates an expression, it uses nothing but the laws of arithmetic and algebra to convert an expression into a value. In ordinary mathematics courses, values are just numbers. We also include symbols, booleans, and indeed all constants:

<val> =<con>

The collection of values is thus just a subset of the collection of expressions.

Now that we have defined the set of values, it is easy to introduce and to explain the evaluation rules. The rules come in two categories: those that appeal to arithmetic knowledge and those that rely on a small amount of algebra. First, we need an infinite number of rules like those of arithmetic to evaluate applications of primitives:

(+ 1 1) = 2 (- 2 1) = 1

But Scheme ``arithmetic'' is more general than just number crunching. It also includes rules for dealing with boolean values, symbols, and lists like these:

(not true) = false

(symbol=? 'a 'b) = false (symbol=? 'a 'a) = true

Second, we need one rule from algebra to understand how the application of a user-defined function advances computation. Suppose the Definitions window contains the definition

(define (f x-1 ... x-n) exp)

and f, x-1, ..., x-n are variables and exp is some (legal) expression. Then an application of a function is governed by the law:

(f v-1 ... v-n) =exp with all x-1 ... x-n replaced by v-1 ... v-n

where v-1 ... v-n is a sequence of values that is as long as x-1 ... x-n.

This rule is as general as possible, so it is best to look at a concrete example. Say the definition is

(define (poly x y) (+ (expt 2 x) y))

Then the application (poly 3 5) can be evaluated as follows:

(poly 3 5)

These last two steps follow from plain arithmetic.

Third and finally, we need some rules that help us determine the value of cond-expressions. These rules are algebraic rules but are not a part of the standard algebra curriculum:

cond_false:

when the first condition is false:

(cond

then the first cond-line disappears;

cond_true:

when the first condition is true:

(cond

[true exp]

...) = exp

the entire cond-expressions is replaced by the first answer;

cond_else:

when the only line left is the else-line:

(cond

[else exp]) = exp

the cond-expressions is replaced by the answer in the else-clause.

No other rules are needed to understand cond.

Consider the following evaluation:

(cond

[false 1]

[true (+ 1 1)]

[else 3])

= (cond

[true (+ 1 1)]

[else 3])

= (+ 1 1)

= 2

It first eliminates a cond-line and then equates the cond-expression with (+ 1 1). The rest is plain arithmetic again.

The rules are equations of the form that we use in arithmetic and algebra on a daily basis. Indeed, the same laws apply to this system of equations as to those in

mathematics. For example, if a = b and b = c, then we also know that a = c. A consequence is that as we get better at hand-evaluations, we can skip obvious steps and combine several equational inferences into one. Here is one shorter version of the previous evaluation:

(cond

[false 1]

[true (+ 1 1)]

[else 3])

= (+ 1 1)

= 2

Even more importantly, we can replace any expression by its equal in every context --just as in algebra. Here is a another cond-expression and its evaluation:

(cond

[( = 1 0 ) 0]

[else (+ 1 1)])

;; The underlined expression is evaluated first.

= (cond

[false 0]

[else (+ 1 1)])

;; Here cond_false applies.

= (cond

[else (+ 1 1)])

;; Using cond_else, we now get an arithmetic expression.

= (+ 1 1)

= 2

For the first step, we evaluated the nested, underlined expression, which is clearly essential here, because no cond rule would apply otherwise. Of course, there is nothing unusual about this kind of computing. We have done this many times in algebra and in the first few sections of this book.

Exercise 8.3.1. Evaluate the following expressions step by step:

1. (+ (* (/ 12 8) 2/3)

Exercise 8.3.2. Suppose the Definitions window contains

;; f : number number -> number (define (f x y)

(+ (* 3 x) (* y y)))

Show how DrScheme evaluates the following expressions, step by step:

1. (+ (f 1 2) (f 2 1)) 2. (f 1 (* 2 3))

3. (f (f 1 (* 2 3)) 19) ;

Solution

8.4

Errors

Parenthesized sentences may or may not belong to Scheme, depending on whether or not they are legal according to the grammar in figure 21. If DrScheme verifies that a sentence does not belong to the language dubbed Beginning Student, it signals a

SYNTAXERROR.

The remaining expressions are syntactically legal, but some of those may still pose problems for our evaluation rules. We say that such legal expressions contain LOGICAL ERRORS or RUN-TIMEERRORS. Consider the simplest example: (/ 1 0). We already know from mathematics that

does not have a value. Clearly, since Scheme's calculations must be consistent with mathematics, it too must not equate (/ 1 0) with a value.

In general, if an expression is not a value and if the evaluation rules allow no further simplification, we say that an error occurred or that the function raises an error signal.

Pragmatically this means that the evaluation stops immediately with an appropriate error message, such as "/: divide by zero" for division by zero.

For an example, consider the following evaluation:

(+ (* 20 2) (/ 1 (- 10 10)))

= (+ 40 (/ 1 0))

= /: divide by zero

The error eliminates the context (+ 40 ...) around (/ 1 0), which represents the remainder of the computation with respect to the division.

To understand how run-time errors are signaled, we must inspect the evaluation rules again. Consider the function

;; my-divide : number -> number (define (my-divide n)

(cond

[(= n 0) 'inf]

[else (/ 1 n)]))

Now suppose we apply my-divide to 0. Then the first step is:

(my-divide 0)

= (cond

[(= 0 0) 'inf]

[else ( / 1 0 ) ])

It would obviously be wrong to say that the function signals the error ``/: divide by zero'' now, even though an evaluation of the underlined subexpression would demand it. After all, (= 0 0) is true and therefore the application has a proper result:

(my-divide 0)

= (cond

[(= 0 0) 'inf]

[else ( / 1 0 ) ])

= (cond

[true 'inf]

[else ( / 1 0 ) ])

= 'inf

Fortunately, our laws of evaluation take care of these situations automatically. We just need to keep in mind when the laws apply. For example, in

(+ (* 20 2) (/ 20 2))

the addition cannot take place before the multiplication or division. Similarly, the underlined division in

(cond

[(= 0 0) 'inf]

[else ( / 1 0 ) ])

cannot be evaluated until the corresponding line is the first condition in the cond-expression.

As a rule of thumb, it is best to keep the following in mind:

In document How to Design Programs (Page 118-123)