图 12.10 展示了如何用结构来实现双向链表。将 看成一种结构,它有两个字段:指向数据的 car
和指向下一个元素的 cdr
。要实现一个双向链表,我们需要第三个字段,用来指向前一个元素。图 12.10 中的 defstruct
定义了一个含有三个字段的对象 dl
(用于“双向链接”),我们将用它来构造双向链表。dl
的 data
字段对应一个 cons
的 car
,next
字段对应 cdr
。 prev
字段就类似一个 cdr
,指向另外一个方向。(图 12.11 是一个含有三个元素的双向链表。) 空的双向链表为 nil
,就像空的列表一样。
图 12.10: 构造双向链表
为了便于操作,我们为双向链表定义了一些实现类似 car
, cdr
, consp
功能的函数:dl-data
, dl-next
和 。 dl->list
是 dl
的打印函数(print-function
),其返回一个包含 dl
所有元素的普通列表。
函数 dl-insert
就像针对双向链表的 cons
操作。至少,它就像 cons
一样,是一个基本构建函数。与 cons
不同的是,它实际上要修改作为第二个参数传递给它的双向链表。在这种情况下,这是自然而然的。我们 cons
内容到普通列表前面,不需要对普通列表的 rest
(译者注: rest
即 cdr
的另一种表示方法,这里的 rest
是对通过 cons
构建后列表来说的,即修改之前的列表) 做任何修改。但是要在双向链表的前面插入元素,我们不得不修改列表的 rest
(这里的 rest
即指没修改之前的双向链表) 的 prev
字段来指向这个新元素。
几个普通列表可以共享同一个尾端。因为双向链表的尾端不得不指向它的前一个元素,所以不可能存在两个双向链表共享同一个尾端。如果 dl-insert
不具有破坏性,那么它不得不复制其第二个参数。
函数 dl-list
是对于 dl
的类似 list
的功能。它接受任意多个参数,它会返回一个包含以这些参数作为元素的 dl
:
它使用了 reduce
函数 (并设置其 from-end
参数为 true
,initial-value
为 nil
),其功能等价于
如果将 dl-list
定义中的 #'dl-insert
换成 #'cons
,它就相当于 list
函数了。下面是 dl-list
的一些常见用法: