如果这看起很熟悉的话,这是应该的。这就是我们一直交谈的那个 eval 。下面这个函数实现了与顶层非常相似的东西:

    也是因为这个原因,顶层也称为读取─求值─打印循环 (read-eval-print loop, REPL)。

    调用 eval 是跨越代码与列表界线的一种方法。但它不是一个好方法:

    对于程序员来说, eval 的主要价值大概是作为 Lisp 的概念模型。我们可以想像 Lisp 是由一个长的 cond 表达式定义而成:

    许多表达式由预设子句 (default clause)来处理,预设子句获得 car 所引用的函数,将 cdr 所有的参数求值,并返回将前者应用至后者的结果。

    但是像 (quote x) 那样的句子就不能用这样的方式来处理,因为 quote 就是为了防止它的参数被求值而存在的。所以我们需要给 quote 写一个特别的子句。这也是为什么本质上将其称为特殊操作符 (special operator): 一个需要被实现为 eval 的一个特殊情况的操作符。

    而如果你将 nil 作为第一个参数传给 compile ,它会编译作为第二个参数传入的 lambda 表达式。

    由于 coercecompile 可接受列表作为参数,一个程序可以在动态执行时 (on the fly)构造新函数。但与调用 eval 比起来,这不是一个从根本解决的办法,并且需抱有同样的疑虑来检视这两个函数。

    函数 eval , 与 compile 的麻烦不是它们跨越了代码与列表之间的界线,而是它们在执行期做这件事。跨越界线的代价昂贵。大多数情况下,在编译期做这件事是没问题的,当你的程序执行时,几乎不用成本。下一节会示范如何办到这件事。