Original |
|
JavaScript |
To evaluate a combination whose operator names a compound procedure, the
interpreter follows much the same process as for combinations whose
operators name primitive procedures, which we described in
section [1.1.3].
|
|
To evaluate a function application, the interpreter follows the process
described in section [1.1.4].
|
That is, the interpreter evaluates the elements of the
combination
application
and applies the
procedure
function
(which is the value of the
operator of the combination)
function expression of the application)
to the arguments (which are the values of the
operands of the combination).
argument expressions of the application).
We can assume that the mechanism for applying primitive
procedures
to arguments is built into the interpreter.
We can assume that the application of primitive
functions is handled by the interpreter or libraries.
For compound
procedures,
functions,
the application process is as follows:
-
To apply a compound
procedure
function
to arguments,
evaluate the body of the procedure
evaluate the return expression of the function
with each
formal
parameter replaced by the corresponding argument.
To illustrate this process, let's evaluate the
combination
application
Original |
JavaScript |
(f 5)
|
f(5)
|
where
f is the
procedure defined
function declared
in section
[1.1.4].
We begin by retrieving the
body
return expression
of
f:
Original |
JavaScript |
(sum-of-squares (+ a 1) (* a 2))
|
sum_of_squares(a + 1, a * 2)
|
Then we replace the parameter
a
by the argument 5:
Original |
JavaScript |
(sum-of-squares (+ 5 1) (* 5 2))
|
sum_of_squares(5 + 1, 5 * 2)
|
Thus the problem reduces to the evaluation of
a combination
an application
with two
operands
arguments
and
an operator sum-of-squares.
a function expression
sum_of_squares.
Evaluating this
combination
application
involves three subproblems. We must evaluate the
operator
function expression
to get the
procedure
function
to be applied, and we must evaluate the
operands
argument expressions
to get the arguments. Now
(+ 5 1)
5 + 1
produces 6 and
(* 5 2)
5 * 2
produces 10, so we must apply the
sum-of-squares procedure
sum_of_squares function
to 6 and 10. These values are substituted for the
formal
parameters
x and
y in the body of
sum-of-squares,
sum_of_squares,
reducing the expression to
Original |
JavaScript |
(+ (square 6) (square 10))
|
square(6) + square(10)
|
If we use the
definition
declaration
of
square, this reduces to
Original |
JavaScript |
(+ (* 6 6) (* 10 10))
|
(6 * 6) + (10 * 10)
|
which reduces by multiplication to
Original |
JavaScript |
(+ 36 100)
|
36 + 100
|
and finally to
Original |
JavaScript |
136
|
136
|
The process we have just described is called the substitution
model for
procedure
function
application. It can be taken as a model that
determines the meaning
of
procedure
function
application, insofar as the
procedures
functions
in this chapter are concerned. However, there are two
points that should be stressed:
-
The purpose of the substitution is to help us think about
procedure
function
application, not to provide a description of how the interpreter
really works. Typical interpreters do not evaluate
procedure
function
applications by manipulating the text of a
procedure to substitute values for the formal
function to substitute values for the
parameters. In practice, the
substitution
is
accomplished by using a local environment for the
formal
parameters. We will discuss this more fully in chapters 3 and
4 when we examine the implementation of an interpreter in detail.
-
Over the course of this book, we will present a sequence of
increasingly elaborate models of how interpreters work, culminating
with a complete implementation of an interpreter and compiler in
chapter
[5]. The substitution model is only the first of
these models—a way to get started thinking formally
about the evaluation process. In general, when
modeling phenomena in science and engineering, we begin with
simplified, incomplete models. As we examine things in greater detail,
these simple models become inadequate and must be replaced by more
refined models. The substitution model is no exception. In particular,
when we address in chapter [3] the use of
procedures
functions
with mutable data,
we will see that the substitution
model breaks down and must be replaced by a more complicated model of
procedure
function
application.
Applicative order versus normal order
According to the description of evaluation given in
section [1.1.3],
section [1.1.4],
the interpreter first evaluates the
operator
function
and
operands
argument expressions
and then applies the resulting
procedure
function
to the resulting arguments. This is not the only way to perform evaluation.
An alternative evaluation model would not evaluate the
operands
arguments
until their values were needed. Instead it would first substitute
operand
argument
expressions for parameters until it obtained an expression involving
only
primitive operators,
operators and primitive functions,
and would then perform the evaluation. If we
used this method, the evaluation of
Original |
JavaScript |
(f 5)
|
f(5)
|
would proceed according to the sequence of expansions
Original |
JavaScript |
(sum-of-squares (+ 5 1) (* 5 2))
(+ (square (+ 5 1)) (square (* 5 2)) )
(+ (* (+ 5 1) (+ 5 1)) (* (* 5 2) (* 5 2)))
|
sum_of_squares(5 + 1, 5 * 2)
square(5 + 1) + square(5 * 2)
(5 + 1) * (5 + 1) + (5 * 2) * (5 * 2)
|
followed by the reductions
Original |
JavaScript |
(+ (* 6 6) (* 10 10))
(+ 36 100)
136
|
6 * 6 + 10 * 10
36 + 100
136
|
This gives the same answer as our previous evaluation model, but the
process is different. In particular, the evaluations of
(+ 5 1)
5 + 1
and
(* 5 2)
5 * 2
are each performed twice here, corresponding to the reduction of the
expression
Original |
JavaScript |
(* x x)
|
x * x
|
with
x replaced respectively by
(+ 5 1)
5 + 1
and
(* 5 2).
5 * 2.
This alternative fully expand and then reduce
evaluation method is known as
normal-order evaluation, in contrast to the evaluate
the arguments and then apply
method that the interpreter actually
uses, which is called
applicative-order evaluation. It can be shown that, for
procedure
function
applications that can be modeled using substitution (including all the
procedures
functions
in the first two chapters of this book) and that yield legitimate values,
normal-order and applicative-order evaluation produce the same value.
(See exercise [1.5]
for an instance of an illegitimate
value where normal-order
and applicative-order evaluation do not give the same result.)
Lisp
JavaScript
uses applicative-order evaluation, partly because of the
additional efficiency obtained from avoiding multiple evaluations of
expressions such as those illustrated with
(+ 5 1)
and (* 5 2)
5 + 1
and 5 * 2
above and, more significantly, because normal-order evaluation
becomes much more complicated to deal with when we leave the realm of
procedures
functions
that can be modeled by substitution. On the other hand,
normal-order evaluation can be an extremely valuable tool, and we will
investigate some of its implications in chapters 3 and 4.