学习 Clojure/操作
Lisp 源代码在两个阶段执行
- 源代码由读取器读取,读取器将源代码解析为数据结构。
- 然后,评估器遍历此数据结构,根据一组简单的规则将数据解释为操作。
虽然几乎所有语言的源代码都是通过首先将其转换为数据结构(抽象语法树 (AST))来处理的,但 Lisp 的独特之处在于它使此数据结构和评估规则足够简单,以便您,使用该语言的程序员,可以完全了解此过程的细节。一旦您了解了读取器和评估器如何“思考”,您就理解了 Lisp。
与其他动态语言一样,Lisp 可以在交互式命令行模式下使用。Lisp 将此模式称为读取评估打印循环 (REPL):每次在 REPL 中键入命令时,它都会被读取并评估,然后评估返回的值会被打印。REPL 可用作计算器,用于快速实验,或用于检查或修改正在运行的程序(例如,调试时触发断点时)。
但是,程序当然是以源代码文件的形式编写的。您可能期望读取器在将代码传递给评估器之前先读取整个源文件,但事实并非如此:在某些 Lisp 方言中,读取器的行为可以被评估的代码修改,因此读取器在读取时将每块可用的代码传递给评估器,从而允许代码修改它下面的代码的读取方式。在 Clojure 中,读取器的行为目前不可修改,但 Clojure 仍然遵循这种传统的读取评估模式。(将来,Clojure 可能允许修改读取器。)
在 Clojure 中,读取器会忽略“;”和该行之后的所有内容。
; this is a comment
空格是将“原子”(符号、关键字、数字文字和字符串文字等)彼此分隔所必需的,但其他情况下会忽略。, 字符被视为空格,这对风格来说很有用,可以更清楚地视觉上分隔原子。
foo, bar ; the comma is treated the same as if it were a space
但在字符串文字中,空格、; 和 , 自然会被视为它们本身。
"hi, there"
否则,读取器基本上将源代码视为一堆文字。例如
(def jerry [1 2 3])
读取器将其解析为一个列表,其中包含符号 def,然后是符号 jerry,最后是一个包含整数 1、2 和 3 的向量。因为此列表位于代码的“顶层”(它不包含在另一个集合中),所以一旦它被完全读取,它就会立即传递给评估器。
读取器的行为可以通过称为读取器宏的文字上的特殊前缀进行修改,但这些基本上只是便利功能,因此我们将在后面讨论它们。