10

http://www.aiai.ed.ac.uk/~jeff/lisp/cl-pitfalls将其称为 Common Lisp 陷阱之一

您认为会修改 CDR 的破坏性函数可能会修改 CAR。(例如,NREVERSE。)

我不确定我应该采取什么预防措施。我可以从 NREVERSE 可能修改 CDR 的事实中采取的通常预防措施是仅在列表(参数)不与我的变量稍后可能引用的任何其他列表共享尾部时使用 NREVERSE(除了我保存返回值的变量到)。NREVERSE 可能会修改 CAR,我应该采取什么预防措施?这有什么要注意的?

4

2 回答 2

12

没有任何上下文,这很难理解。

例子:

(setq list1 (list 1 2 3 4))

我们现在有一个包含四个数字的列表。变量list1指向第一个缺点。

如果我们看一个破坏性的反转,我们正在谈论一个可能改变 cons 单元的操作。有不同的方法可以颠倒这个列表。

例如,我们可以取 cons 单元格并反转它们。然后第一个 cons 单元格是最后一个。然后必须将该 cons 单元的 cdr 更改为NIL.

CL-USER 52 > (setq list1 (list 1 2 3 4))
(1 2 3 4)

CL-USER 53 > (nreverse list1)
(4 3 2 1)

现在我们的变量list1仍然指向同一个 cons 单元格,但是它的 cdr 已经改变了:

CL-USER 54 > list1
(1)

为了确保变量指向一个反向列表,程序员有责任更新变量并将其设置为nreverse操作的结果。人们也可能试图利用list1指向最后一个缺点的可观察结果。

以上是 Lisp 开发人员通常所期望的。大多数反向实现似乎都是这样工作的。但是 ANSI CL 标准中没有规定如何nreverse实现。

那么,改变 CAR 意味着什么呢?

让我们看一下的替代实现nreverse

(defun nreverse1 (list)
  (loop for e across (reverse (coerce list 'vector))
        for a on list do
        (setf (car a) e))
  list)

上面的函数让我们的 cons 细胞链完好无损,但改变了car

CL-USER 56 > (setq list1 (list 1 2 3 4))
(1 2 3 4)

现在让我们使用新版本,nreverse1.

CL-USER 57 > (nreverse1 list1)
(4 3 2 1)

CL-USER 58 > list1
(4 3 2 1)

现在你看到了不同之处:list1仍然指向整个列表。

摘要:需要注意的是,可能有不同的实现nreverse。不要利用通常的行为,其中一个变量将指向最后一个缺点。只需使用的结果,nreverse一切都很好。

旁注:第二个版本可以在哪里使用?

Lisp 机器上的一些 Lisp 实现允许列表的紧凑型向量表示。如果在这样的 Lisp 实现中,可以对这样的列表进行 nreverse,则实现者可以提供一个有效的类似向量的 nreverse。

于 2013-08-18T17:56:14.560 回答
3

在任何情况下,无论是修改了 cons 单元的 CAR 还是 CDR,如果传递列表的任何 cons 单元(包括第一个 cons 单元)可能与另一个列表共享,则不应使用 NREVERSE。改为使用 REVERSE。

顺便说一句,clisp 确实修改了 CAR:

> (let ((a (list 1 2 3 4 5 6 7 8 9 0)))
     (nreverse a)
     a)
(0 9 8 7 6 5 4 3 2 1)
于 2013-08-19T02:08:09.667 回答