Appearance
第七课:对子
最后我们来看 Lisp 最核心的东西——对子。简单来说,对子就是由两个东西组合在一起构成的东西,类似数学上的“有序数对”。在 Lisp 中,使用 cons
过程来构造一个对子。
scheme
> (cons 1 2)
'(1 . 2)
解释器输出了 '(1 . 2)
,就代表这是一个用 1
和 2
组合起来的对子。(感觉很像列表的输出格式?别急,往下看看就知道。)
对子是有序的。用 car
过程输出对子的左边的部分;用 cdr
过程输出对子的右边的部分。
scheme
> (define x (cons 1 2))
> (car x)
1
> (cdr x)
2
你可能大脑已经开始飞速旋转了——怎么这个和列表的那个操作都叫 car
cdr
呢?函数重载吗?还是什么特殊的语法?……
好了不卖关子了,其实列表就是由一系列对子构成的。看这个例子:
scheme
> (cons 42 (cons 69 (cons 613 '())))
'(42 69 613)
当我连续用三个 cons
过程构建出三个对子后,解释器直接将它解释为一个列表。首先,第一个对子的左半部分是 42
,右半部分是一个对子;这个对子的左半部分是 69
,右半部分又是一个对子;最后这个对子的左半部分是 613
,右半部分是空表。当一系列对子以“右半部分”链接起来且最后一个右半部分是空表的时候,那么它就是一个列表。列表中的元素,就是这些对子的左半部分的值。
因此,将 car
作用在列表上,输出的正是第一个元素。因为列表作为对子的左半部分,就是第一个元素。将 cdr
作用在列表上,输出的就是除首元素外的列表,因为第一个对子的 cdr
就是这样的一个列表。
TIP
这种由对子构建列表的方式非常像其它编程语言中常见的“链表”。
接下来的内容和使用 Lisp 编程已经没有太大关系了,但如果你想编写一个 Lisp 解释器,那么可以继续读下去。
对子是 Lisp 中唯一的复合数据类型。其余的数据类型,比如整数 42
、有理数 5/2
、浮点数 3.14
、布尔值 #t
,都是原子类型,即不可再分的。而基于对子,不仅可以构建出列表,还可以结合一些语言特性构建出更复杂的数据结构(比如树、散列表等等)。
列表不仅构成 Lisp 程序的大量数据结构,也是 Lisp 源码的结构。不论是特殊形式,还是组合式,它们不都写成列表的样子吗(少了一个单引号)?
scheme
(define x 42) ; 看上去很像由 define、x 和 42 构成的列表
(+ x 1) ; 看上去很像由 +、x 和 1 构成的列表
它们不仅是列表的样子,它们实际上就是列表。用源码 (define x 42)
举例;它其实是由符号 define
、符号 x
和整数 42
构成的列表。
这里提到了“符号”。符号是其它编程语言中很少出现的概念。在 Lisp 中,你可以将符号直接理解为一种特殊的字符串类型——用作变量名的字符串,目前就先这样理解好了。使用一个单引号再加上变量名,就可以或得到表示这个变量名的符号:
scheme
> 'define
'define
如果将符号 '+
、符号 'x
和整数 1
构成一个列表,那么你就得到了源码 (+ x 1)
的表示形式。把源码的表示形式塞到 eval
过程中,就可以直接对这个源码求值!
scheme
> (define x 42)
> (define source (list '+ 'x 1))
> (eval source)
43
从这个案例我们可以一窥 Lisp 解释器的构造。假设 Lisp 解释器正在解释一个列表形式的源码。如果列表的 car
是一个符号,而且符号名指代了特殊形式如 'define
,那么执行相关的特殊逻辑。否则,对列表中的每一个元素进行求值。
- 对整数、布尔类型的求值结果仍然是它自身——这种元素类型称为自求值类型。
- 对符号类型的求值结果,就是查找当前环境定义的所有变量名,找到这个符号名对应的值作为求值结果。
- 对列表类型求值,就是调用过程的逻辑了。将列表的
car
部分作为要调用的过程,将cdr
部分作为实参,然后绑定到对应形参,执行过程的定义。
最后讲一个特殊形式 quote
。quote
特殊形式接受一个东西,不做任何求值操作直接原样返回。比如:
scheme
> (quote (+ 1 2))
'(+ 1 2) ; 由符号 +、整数 1 和整数 2 构成的列表
quote
特殊形式还可以简写成 '
。因此 '(1 2 3)
就是 (quote (1 2 3))
—— 解释器就解释为 '(1 2 3)
。哈哈,绕了一圈又回到起点。这就能解释为什么解释器输出的列表长成 '( ... )
的样子,因为你直接把这个样子作为源码输入到解释器,得到的就是这个东西。
TIP
思考题:请问 (car ''x)
求值得到的结果是什么?为什么?试一试,想一想。