Appearance
第五课:列表
在 Lisp 中,最常用的数据结构就是列表。列表顾名思义就是一系列值的组合,类似于 Python 中的 list
,C++ 中的 std::vector
。
构建一个列表的方法是使用 list
过程:
scheme
> (list 1 4 2 8)
'(1 4 2 8)
解释器返回的结果 '(1 4 2 8)
就代表一个列表——用一个单引号,外加一对括号;内部是空格分隔的值。
TIP
这种表示方法很像组合式——其实这不是巧合,我们之后再讲。
如何访问一个列表的元素呢?事实上 Lisp 没有直接提供访问元素的方法。Lisp 提供两个过程 car
cdr
,可以分别获取列表的首元素和除去首元素外的其余部分构成的新列表。
scheme
> (define x (list 1 4 2 8))
> (car x)
1
> (cdr x)
'(4 2 8)
如果列表只有一个元素,那么将 cdr
作用在其上会得到空表 '()
。此外,(list)
的结果也是 '()
。空表是一个特殊值,不能再被 car
或 cdr
作用。
scheme
> (cdr (list 1))
'()
> (list)
'()
其实这一课关于语法的内容只有这么多了。(如果你着急的话,可以直接翻到 下一课。)但是感觉什么都没讲?来看看下面的代码:
scheme
; 从列表 l 获取第 i 个元素(0 起始)
(define (at l i)
(if (equal? i 0)
(car l)
(at (cdr l) (- i 1))))
这里用到了一个没见过的内置过程 equal?
,我稍微解释一下,就是比较两个操作元素是否相等。从而,整段程序可以解读为:
- 定义过程
at
:接受两个实参l
和i
(其中l
是列表,i
是整数,意思是取l
的第i
个元素)。过程是这样执行的:- 判断
i
是否等于零;- 如果是零的话,就返回
(car l)
:也就是l
的首个元素; - 否则,就返回
(at (cdr l) (- i 1))
——就是(cdr l)
的第 i - 1 个元素。
- 如果是零的话,就返回
- 判断
有感觉了吗?这里用到的是对 at
的递归。由于 (cdr l)
会“删除” l
的首个元素,因此 l
的第 i 个元素就相当于 (cdr l)
的第 i - 1 个元素。这样递归做下去,就可以遇到 i
为零的时刻,随后用 car
作用一下就可以了。
scheme
> (define my-list (list 1 4 2 8))
> (at my-list 0)
1
> (at my-list 3)
8
通过这个小例子,我们对 Lisp 这种编程语言的运作方式有了一个感性的认识。比如最直观的,在 Lisp 中,通常使用递归来表示循环的逻辑。