2

考虑缺点x1

(setq x1 '(a . (b c))) => (a b c)

或以列表表示法:

(setq x1 '(a b c)) => (a b c)

和缺点x2,建立在x1

(setq x2 (cons 'A (cdr x1))) => (A b c)

conshelp (在 Emacs 中)说该函数创建一个新的 cons,给它参数,'A(cdr x1), 作为组件并返回它。其中没有任何内容表明新返回的 cons 的生命周期将与其生成组件的生命周期相关联。

无论如何,如果有人修改了副本,x2那么原始的缺点也会(a . (b c))被修改:

(setcar (cdr x2) 'B) => B
x2 => (A B c)  ; as from the assignment 
x1 => (a B c)  ; x1 and x2 are linked

其他函数示例可以显示 和 之间的x1联系x2

(setq x1 '(a . (b c))) => (a b c)
(setq x2 (cons 'A (cdr x1))) => (A b c)
(nreverse x2) => (c b A)
x1 => (a b A)

我从 Emacs Lisp 参考手册中的文档中获取了这个示例setcar,其中指出“cons 单元是共享结构的一部分”,并且 cdrx1x2被称为“共享链接” x1x2并且以图形方式显示为(稍微改编):

x1:
   --------------       --------------       --------------
  | car   | cdr  |     | car   | cdr  |     | car   | cdr  |
  |   a   |   o------->|   b   |   o------->|   c   |  nil |
  |       |      |  -->|       |      |     |       |      |
   --------------  |    --------------       --------------
                   |
  x2:              |
   --------------  |
  | car   | cdr  | |
  |   A   |   o----
  |       |      |
   --------------

这让人想起 C 指针,因为 cdrx2不是副本,而是“指向” 的 cdr x1。很清楚,但我想知道这种情况何时实际出现,也就是说,我怎么知道一个 cons 的(一个元素)是指向另一个 cons 还是一个自活的副本?更一般地说,共享结构共享链接 的正式定义是什么(在哪里)?

在 Emacs Lisp 参考手册中没有明确提及它们。事实上,在其索引中搜索“共享”或“链接”只会返回(不包括文件/网络链接)对“共享结构,读取语法”的间接引用,处理它们的表示,而不是它们是什么。
好奇地在 PDF 中搜索“共享”土地,作为第一次出现,到“读取圆形对象的语法”部分,从“表示共享或圆形结构......”开始。不幸的是,之前没有提到共享和循环(结构)这两个词!下一个事件是提到的setcar文档。

所以在 Lisp/Elisp 中似乎有隐含的指针,但没有人愿意告诉他们。))

4

3 回答 3

4

你的朋友是函数eqequal

eq比较物理身份,同时equal检查对象是否“看起来相似”。

在你的情况下:

(defvar a (list 1 2 3))
(defvar b (cons 1 (cdr a)))
(equal a b)
==> t
(eq a b)
==> nil
(eq (cdr a) (cdr b))
==> t

编辑:注意这list相当于几个cons电话

(list x y) == (cons x (cons y nil))

并且每当您调用consor时list,您都会得到与其他任何东西无关 eq东西

继续上面的例子:

(defvar c (list 4 (cdr a)))
(defvar d (list 4 (cdr b)))
(equal c d)
==> t
(eq c d)
==> nil
(eq (cdr c) (cdr d))
==> nil
(eq (cadr c) (cadr d))
==> t
(eq (cadr c) (cdr a))
==> t
(eq (cadr d) (cdr b))
==> t

PS。认识到(E x y) ==> (E (F x) (F y))whereE是一个等式谓词 ( eqor equal) 并且F是一个访问器 (例如caror cdr) 是很有用的。

聚苯乙烯。反之亦然equal

(and (equal (car x) (car y)) 
     (equal (cdr x) (cdr y)))

暗示(实际上,等价于)(equal x y);但不是为了eq

于 2014-02-09T16:16:42.167 回答
0

要添加到@sds 的答案,因为他没有明确提到它,你问过这个:

请参阅 Elisp 手册、节点Modifying Lists及其子节点。您询问的示例在 node 中明确提到Setcar

 ;; Create two lists that are partly shared.
 (setq x1 '(a b c))
      => (a b c)
 (setq x2 (cons 'z (cdr x1)))
      => (z b c)

是的,您的问题不是专门关于setcar和其他列表结构修改功能。但是除了这里给出的关于如何在 Lisp 中传递参数的评论和答案之外,Elisp 手册中关于cons 单元格的介绍提供了您通常正在寻找的答案。

于 2014-02-09T19:28:06.457 回答
0

这里还有一些其他的答案可以更详细地解释这一点,但以非常小的类比为目标也可能会有所帮助。单元格是一个cons非常小的容器:它包含两个元素,并具有访问器carcdr用于将这些元素取出。

在大多数面向对象的编程语言中,将对象放入容器时不会自动复制它们。例如,在 Java 中,如果您有:

Object a = new Object();
Object b = new Object();

Object[] cons1 = new Object[] { a, b };
Object[] cons2 = new Object[] { a, b };

你应该期待

cons1 == cons2 

是假的,但是

( cons1[0] == cons2[1] ) && ( cons1[1] == cons2[1] )

是真实的。容器对象不同,但它们包含的对象是相同的。

这只是一个约定,列表是从 cons 单元格构建的,其中列表是空列表 ( nil),或者car是列表的第一个元素并且cdr是列表的其余部分的 cons 单元格。

这让人想起 C 指针,因为 x2 的 cdr 不是副本,而是“指向”x1 的 cdr。很清楚,但我想知道这种情况何时实际出现,也就是说,我怎么知道一个 cons 的(一个元素)是指向另一个 cons 还是一个自活的副本?更一般地说,共享结构和共享链接的正式定义是什么(在哪里)?

这不仅仅是让人想起,它几乎是一样的。内存中有对象,您可以获取它们。有时不止一件东西可能会引用内存中的同一个对象。cons您可以使用 测试单元格的相等性eq,但这不是关于共享结构的一般答案,因为您无法知道还有谁引用了对象。通常,您将遵循“不要修改您未创建的结构,除非您在文档中明确提及它,即使那样,返回重要值”这样的规则。因此reverse不会修改其参数,但nreverse允许(但仍返回反向列表; nreverse不保证列表原地反转)。如果您正在构建一个对您的函数来说是本地的列表,则可以使用nreverse它来反转它,因为您知道没有其他人引用它。

于 2014-02-10T15:11:53.177 回答