Given the evaluator, we have in our hands a description (expressed in Lisp) of
the process by which Lisp expressions are evaluated. One advantage of
expressing the evaluator as a program is that we can run the program. This
gives us, running within Lisp, a working model of how Lisp itself evaluates
expressions. This can serve as a framework for experimenting with evaluation
rules, as we shall do later in this chapter.
有了这个求值器,我们手边就有了一段用 Lisp 写成的描述——描述 Lisp 表达式是如何被求值的那个计算过程。将求值器表达为程序的一个好处是我们可以运行它。这样,我们便在 Lisp 内部运行着一个能实际展示 Lisp 如何对表达式求值的工作模型。这个模型可以作为试验各种求值规则的框架,正如我们将在本章后面所做的那样。
Our evaluator program reduces expressions ultimately to the application of
primitive procedures. Therefore, all that we need to run the evaluator is to
create a mechanism that calls on the underlying Lisp system to model the
application of primitive procedures.
我们的求值器程序最终将表达式归结为对基本过程的应用。因此,要运行这个求值器,我们所需要做的只是建立一种机制,调用底层 Lisp 系统来模拟对基本过程的应用。
There must be a binding for each primitive procedure name, so that when
eval evaluates the operator of an application of a primitive, it will
find an object to pass to apply. We thus set up a global environment
that associates unique objects with the names of the primitive procedures that
can appear in the expressions we will be evaluating. The global environment
also includes bindings for the symbols true and false, so that
they can be used as variables in expressions to be evaluated.
每个基本过程的名字都必须有一个绑定,这样当 eval 对某个应用的运算符求值,而该运算符是一个基本过程时,它才能找到一个对象传递给 apply。为此,我们建立一个全局环境,将唯一的对象与那些可能出现在我们将要求值的表达式中的基本过程名字关联起来。全局环境中还包含符号 true 和 false 的绑定,使它们可以作为变量在待求值的表达式中使用。
It does not matter how we represent the primitive procedure objects, so long as
apply can identify and apply them by using the procedures
primitive-procedure? and apply-primitive-procedure. We have
chosen to represent a primitive procedure as a list beginning with the symbol
primitive and containing a procedure in the underlying Lisp that
implements that primitive.
基本过程对象的表示方式并不重要,只要 apply 能借助过程 primitive-procedure? 和 apply-primitive-procedure 识别并应用它们即可。我们选择将一个基本过程表示为一个以符号 primitive 开头的表,其中包含底层 Lisp 中实现该基本过程的一个过程。
Setup-environment will get the primitive names and implementation
procedures from a list:
Setup-environment 将从一个表中取得基本过程的名字和实现过程:
To apply a primitive procedure, we simply apply the implementation procedure to
the arguments, using the underlying Lisp
system:
为了应用一个基本过程,我们只需将实现过程应用于这些参数,借助底层 Lisp 系统来完成:
For convenience in running the metacircular evaluator, we provide a
为了方便运行元循环求值器,我们提供一个
driver loop that models the read-eval-print loop of the underlying
Lisp system. It prints a
prompt, reads an input expression,
evaluates this expression in the global environment, and prints the result. We
precede each printed result by an
output prompt so as to distinguish
the value of the expression from other output that may be printed.
驱动循环 (driver loop),它模拟底层 Lisp 系统的读入-求值-打印循环。它打印一个提示符,读入一个输入表达式,在全局环境中对该表达式求值,并打印结果。我们在每个打印结果前加上一个输出提示符,以区分表达式的值与可能被打印的其他输出。
We use a special printing procedure, user-print, to avoid printing the
environment part of a compound procedure, which may be a very long list (or may
even contain cycles).
我们使用一个特殊的打印过程 user-print,以避免打印复合过程的环境部分——那部分可能是一个非常长的表(甚至可能包含循环)。
Now all we need to do to run the evaluator is to initialize the global
environment and start the driver loop. Here is a sample interaction:
现在,要运行求值器,我们所要做的就是初始化全局环境并启动驱动循环。下面是一段示例交互:
(define (setup-environment)
(let ((initial-env
(extend-environment
(primitive-procedure-names)
(primitive-procedure-objects)
the-empty-environment)))
(define-variable! 'true true initial-env)
(define-variable! 'false false initial-env)
initial-env))
(define the-global-environment
(setup-environment)) (define (primitive-procedure? proc)
(tagged-list? proc 'primitive))
(define (primitive-implementation proc)
(cadr proc)) (define primitive-procedures
(list (list 'car car)
(list 'cdr cdr)
(list 'cons cons)
(list 'null? null?)
⟨more primitives⟩ ))
(define (primitive-procedure-names)
(map car primitive-procedures))
(define (primitive-procedure-objects)
(map (lambda (proc)
(list 'primitive (cadr proc)))
primitive-procedures)) (define (apply-primitive-procedure proc args)
(apply-in-underlying-scheme
(primitive-implementation proc) args)) (define input-prompt ";;; M-Eval input:")
(define output-prompt ";;; M-Eval value:")
(define (driver-loop)
(prompt-for-input input-prompt)
(let ((input (read)))
(let ((output
(eval input
the-global-environment)))
(announce-output output-prompt)
(user-print output)))
(driver-loop))
(define (prompt-for-input string)
(newline) (newline)
(display string) (newline))
(define (announce-output string)
(newline) (display string) (newline)) (define (user-print object)
(if (compound-procedure? object)
(display
(list 'compound-procedure
(procedure-parameters object)
(procedure-body object)
'procedure-env>))
(display object))) (define the-global-environment
(setup-environment))
(driver-loop)
;;; M-Eval input:
(define (append x y)
(if (null? x)
y
(cons (car x) (append (cdr x) y))))
;;; M-Eval value:
ok
;;; M-Eval input:
(append '(a b c) '(d e f))
;;; M-Eval value:
(a b c d e f)