Lisp 如何注意NREVERSE可能会修改汽车这一事实

Lisp 如何注意NREVERSE可能会修改汽车这一事实,lisp,common-lisp,Lisp,Common Lisp,指出这是一个常见的Lisp陷阱 您认为可能会修改CDR的破坏性函数 改为改装汽车。(例如,反面) 我不确定我应该采取什么预防措施。从NREVERSE可能修改CDR这一事实中,我可以采取的通常预防措施是,仅当列表(参数)与我的变量稍后可能引用的任何其他列表(保存返回值的变量除外)不共享尾部时,才使用NREVERSE。NREVERSE可能会修改汽车,我应该采取什么预防措施?这有什么值得注意的吗?没有任何上下文,这很难理解 例如: (setq list1 (list 1 2 3 4)) 我们现在有一

指出这是一个常见的Lisp陷阱

您认为可能会修改CDR的破坏性函数 改为改装汽车。(例如,反面)


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

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

例如:

(setq list1 (list 1 2 3 4))
我们现在有一个由四个数字组成的列表。变量
list1
指向第一个cons

如果我们看一个破坏性的反转,我们谈论的是一个可能改变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

那么换辆车意味着什么呢

让我们看看
nreverse
的另一种实现:

(defun nreverse1 (list)
  (loop for e across (reverse (coerce list 'vector))
        for a on list do
        (setf (car a) e))
  list)
上面的函数让我们完整地保存cons单元链,但会更改汽车

现在让我们使用新版本,
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的有效向量。

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

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

> (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)

我喜欢你的回答,因为我喜欢你所采取的详细方法,但我不确定你是否回答了OP的问题,即如何处理
car
cdr
在调用
nreverse
@Chris Jester-Young时都可能发生变异的事实:没有必要处理它。只是不要利用特定实现的特定副作用。只需使用返回的
nreverse
。我同意,如果您传递的列表没有别名。否则,你会遇到各种各样的麻烦。特别是Scheme,它有许多返回共享尾部结果的列表过程,这是一种别名形式。例如,
append
append
ncoc
)与最后给出的列表共享尾部,因此调用
反向
nreverse
)这可能会有问题(如果在其他地方使用带有尾部共享的列表)。@Chris Jester Young:当然,nreverse通常会有明显的副作用,从而导致问题。这里的要点是,这些副作用可能不同于预期,因为可以使用一种相对罕见的nreverse实现变体。@Chris Jester Young:Common Lisp中的nreverse不一定是非考虑性的。CLHS:nreverse可以创建新序列,修改参数序列,或者两者都可以。创建一个新的序列将是一个需要考虑的问题。只是我们希望在任何自尊的实现中,使用nreverse比反向有一些优势。
> (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)