第 11 课导言
介绍求值器的概念,以及我们将要构建的元循环求值器的基本原理。
元循环求值器
还记得第 6 课中的 Racket-1/Scheme-1 吗?现在,是时候探索 Racket 和 Scheme 是如何对表达式求值的了!
你可以在终端中输入以下命令来下载本课的代码:
cp ~cs61as/lib/mceval.scm .
代码也可以在网上获取。
预备知识与学习预期
透彻理解 Racket-1/Scheme-1 的工作方式,对本课学习很有帮助。你还应当熟悉第 8 课中介绍的求值环境模型。本课的内容与前面学过的内容有相当大的差异,要做好心理准备!
阅读材料
本课的相关阅读材料如下:
什么是求值器?
到目前为止,我们已经学会了如何编写输出我们所需结果的过程。一旦定义了这些过程,并把它们输入 Scheme 的提示符,就能得到值。但是,你有没有想过,这些过程究竟是怎样被求值、怎样运转起来的?Scheme 怎么知道一个表达式是什么意思?这些正是求值器(evaluator)要解决的问题。
某种编程语言的求值器(也叫解释器(interpreter))是这样的一个过程:把它应用于该语言的一个表达式时,它会执行求值这个表达式所需的全部动作。
等等,什么?求值器只是一个过程?
没错。求值不过是又一个程序而已!
什么是元循环求值器?
我们的 Scheme 求值器将用一个 Scheme 程序来实现。用本身由 Scheme 写成的求值器去求值 Scheme 程序,听起来像是循环定义。然而,求值本身是一个过程,用 Scheme 来描述求值过程是恰当的——毕竟,Scheme 就是我们描述过程的工具。如果求值器所用的语言与它求值的语言相同,就称之为元循环(metacircular)求值器。
元循环求值器本质上是用 Scheme 对第 8 课所述求值环境模型的一种公式化表述。回忆一下,该模型由两个基本部分组成:
- 对一个组合式(compound expression,即除特殊形式以外的复合表达式)求值时,先求值各个子表达式,然后把运算符子表达式的值应用于各运算数子表达式的值。
- 把复合过程应用于一组实参时,在一个新环境中求值过程体。为构造这个新环境,需要将过程对象中的环境部分用一个框架扩展,在该框架中,过程的形参与过程被应用时的实参绑定。
这两条规则描述了求值过程的本质,即一个基本循环:待求值的表达式在环境中被归约为要应用于实参的过程,而这些过程又依次归约为要在新环境中求值的新表达式,如此反复,直到最后归结到符号(其值在环境中查找)和原语过程(primitive procedure,直接被应用)。这个求值循环将由求值器中两个关键过程 eval 和 apply 的相互作用来体现。我们很快就会详细了解 eval 和 apply。
求值器的实现依赖于一些定义待求值表达式语法的过程。我们将使用数据抽象,使求值器不依赖语言的具体表示方式。例如,我们不会断言赋值必须表示成以符号 set! 开头的表,而是用一个抽象谓词 assignment? 来检验是否为赋值,并用抽象选择器 assignment-variable 和 assignment-value 来访问赋值的各个部分。我们还将学习规定过程和环境表示方式的表达式与操作的实现。例如,make-procedure 构造复合过程,lookup-variable-value 访问变量的值,apply-primitive-procedure 把原语过程应用于给定的实参表。
要点
在本小节中,你学到了:
- 求值器的定义
- 元循环求值器的定义
接下来
现在就开始真正理解 Scheme 的运作机制吧!很激动人心!