通常,Lisp 中的阅读器读取 s 表达式并返回数据结构。 READ 是 I/O 操作:输入是字符流,输出是 Lisp 数据。
printer 则相反:它获取 Lisp 数据并将其作为字符流输出。因此它也可以将 Lisp 数据打印到外部 s 表达式中。
请注意,解释意味着特定的东西:由解释器执行代码。但是许多 Lisp 系统(包括 Clojure)都在使用编译器。为 Lisp 表单计算值的任务通常称为求值。评估可以通过解释、编译或两者混合来实现。
S-Expression:符号表达式。数据的外部文本表示。外部意味着 s 表达式是您在文本文件、字符串等中看到的内容。所以 s 表达式由某些(通常是外部)介质上的字符组成。
Lisp 数据结构:符号、列表、字符串、数字、字符……
Reader:读取 s 表达式并返回 Lisp 数据结构。
请注意,s 表达式也用于编码 Lisp 源代码。
在某些 Lisp 方言中,阅读器是可编程的和表驱动的(通过所谓的阅读表)。此读取表包含字符的读取器函数。例如,quote ' 字符绑定到一个函数,该函数读取一个表达式并返回 (list 'quote expression) 的值。数字字符 0..9 绑定到读取数字的函数(实际上这可能更复杂,因为一些 Lisps 允许以不同的基数读取数字)。
S 表达式为数据结构提供外部语法。
Lisp 程序是使用 s 表达式以外部形式编写的。但并非所有 s 表达式都是有效的 Lisp 程序:
(if a b c d e) is usually not a valid Lisp program
Lisp 的语法通常是在 Lisp 数据之上定义的。
IF 具有例如以下语法(在 Common Lisp http://www.lispworks.com/documentation/HyperSpec/Body/s_if.htm 中):
if test-form then-form [else-form]
所以它需要一个 test-form、一个 then-form 和一个可选的 else-form。
作为 s 表达式,以下是有效的 IF 表达式:
(if (foo) 1 2)
(if (bar) (foo))
但由于 Lisp 程序是表单,我们也可以使用 Lisp 程序构造这些表单:
(list 'if '(foo) 1 2) 是一个返回有效 IF 形式的 Lisp 程序。
CL-USER 24 > (describe (list 'if '(foo) 1 2))
(IF (FOO) 1 2) is a LIST
0 IF
1 (FOO)
2 1
3 2
这个列表例如可以用 EVAL 来执行。 EVAL 需要列表形式 - 而不是 s 表达式。请记住,s 表达式只是一种外部表示。要创建 Lisp 表单,我们需要阅读它。
这就是为什么说代码就是数据。 Lisp 形式表示为内部 Lisp 数据结构:列表、符号、数字、字符串……在大多数其他编程语言中,代码是原始文本。在 Lisp 中,s 表达式是原始文本。使用函数 READ 读取时,s 表达式将转换为数据。
因此,Lisp 中的基本交互顶层称为 REPL,Read Eval Print Loop。
它是一个循环读取 s 表达式,计算 lisp 形式并打印出来:
READ : s-expression -> lisp data
EVAL : lisp form -> resulting lisp data
PRINT: lisp data -> s-expression
所以最原始的REPL是:
(loop (print (eval (read))))
因此,从概念的角度来看,为了回答您的问题,在评估期间,读者什么都不做。它不参与评估。评估由函数 EVAL 完成。通过调用 READ 调用阅读器。由于 EVAL 使用 Lisp 数据结构作为输入(而不是 s 表达式),读取器在 Lisp 表单被评估之前运行(例如通过解释或通过编译和执行它)。