In thinking about a Lisp program that evaluates Lisp expressions, an analogy
might be helpful. One operational view of the meaning of a program is that a
program is a description of an abstract (perhaps infinitely large) machine.
For example, consider the familiar program to compute factorials:
在思考一个能对 Lisp 表达式求值的 Lisp 程序时,一个类比或许会有所帮助。从操作的角度来看,一个程序的含义是:程序是对一台抽象机器(可能是无限大的)的描述。例如,考虑这个计算阶乘的熟悉程序:
We may regard this program as the description of a machine containing parts
that decrement, multiply, and test for equality, together with a two-position
switch and another factorial machine. (The factorial machine is infinite
because it contains another factorial machine within it.) Figure 4.2 is
a flow diagram for the factorial machine, showing how the parts are wired
together.
Figure 4.2: The factorial program, viewed as an abstract machine.
我们可以把这个程序看作对一台机器的描述,这台机器包含执行减法、乘法和相等测试的部件,以及一个两位置的开关和另一台阶乘机器。(阶乘机器是无限的,因为它内部包含另一台阶乘机器。)图 4.2 是阶乘机器的流程图,展示了各部件是如何连接在一起的。
图 4.2:阶乘程序,视为一台抽象机器。
In a similar way, we can regard the evaluator as a very special machine that
takes as input a description of a machine. Given this input, the evaluator
configures itself to emulate the machine described. For example, if we feed
our evaluator the definition of factorial, as shown in Figure 4.3,
the evaluator will be able to compute factorials.
Figure 4.3: The evaluator emulating a factorial machine.
以类似的方式,我们可以把求值器看作一台非常特殊的机器,它接受一台机器的描述作为输入。给定这个输入,求值器便把自己配置成能模拟所描述机器的行为。例如,如果我们把阶乘的定义输入给求值器,如图 4.3 所示,求值器就能计算阶乘。
图 4.3:求值器模拟一台阶乘机器。
From this perspective, our evaluator is seen to be a
universal machine.
It mimics other machines when these are described as Lisp
programs. This is striking. Try to imagine an analogous evaluator
for electrical circuits. This would be a circuit that takes as input a signal
encoding the plans for some other circuit, such as a filter. Given this input,
the circuit evaluator would then behave like a filter with the same
description. Such a universal electrical circuit is almost unimaginably
complex. It is remarkable that the program evaluator is a rather simple
program.
从这个角度来看,我们的求值器被视为一台通用机器 (universal machine)。当其他机器被描述为 Lisp 程序时,它就能模拟那些机器。这是令人震撼的。试着想象一个类似的电路求值器。那将是一个电路,它接受编码某个其他电路(比如一个滤波器)方案的信号作为输入。给定这个输入,该电路求值器就会表现得像一个具有相同描述的滤波器。这样一个通用电路在想象中几乎是无比复杂的。令人惊叹的是,程序求值器却是一个相当简单的程序。
Another striking aspect of the evaluator is that it acts as a bridge between
the data objects that are manipulated by our programming language and the
programming language itself. Imagine that the evaluator program (implemented
in Lisp) is running, and that a user is typing expressions to the evaluator and
observing the results. From the perspective of the user, an input expression
such as (* x x) is an expression in the programming language, which the
evaluator should execute. From the perspective of the evaluator, however, the
expression is simply a list (in this case, a list of three symbols: *,
x, and x) that is to be manipulated according to a well-defined
set of rules.
求值器的另一个令人震撼之处在于,它充当了我们编程语言所操纵的数据对象与编程语言本身之间的桥梁。设想求值器程序(用 Lisp 实现)正在运行,一个用户正在向求值器输入表达式并观察结果。从用户的角度来看,像 (* x x) 这样的输入表达式是编程语言中的一个表达式,求值器应当执行它。然而从求值器的角度来看,该表达式不过是一个表(在这个例子中,是由三个符号组成的表:*、x 和 x),需要按照一套定义明确的规则来处理。
That the user’s programs are the evaluator’s data need not be a source of
confusion. In fact, it is sometimes convenient to ignore this distinction, and
to give the user the ability to explicitly evaluate a data object as a Lisp
expression, by making eval available for use in programs. Many Lisp
dialects provide a primitive eval procedure that takes as arguments an
expression and an environment and evaluates the expression relative to the
environment. Thus,
用户的程序就是求值器的数据,这一点无需造成混淆。事实上,有时候忽略这一区分反而方便,可以通过在程序中提供 eval,赋予用户显式地将一个数据对象作为 Lisp 表达式求值的能力。许多 Lisp 方言都提供一个基本过程 eval,它以一个表达式和一个环境为参数,在该环境中对表达式求值。因此,
and
以及
will both return 25.
都将返回 25。
(define (factorial n)
(if (= n 1)
1
(* (factorial (- n 1)) n))) (eval '(* 5 5) user-initial-environment) (eval (cons '* (list 5 5))
user-initial-environment)