Emacs Lisp共享结构和共享链接



考虑缺点x1:

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

或列表表示法:

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

以及基于x1:的CONx2

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

cons帮助(在Emacs中)说,函数创建一个新的cons,将参数'A(cdr x1)作为组件,返回它。其中没有任何内容表明新返回的cons的寿命将与其生成组件的寿命链接

无论如何,如果修改副本x2,也修改原始cons,(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

其他函数示例可以显示x1x2之间的链接。

(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 cell是共享结构的一部分",x1x2的cdr被称为"共享链接",而x1x2的图形显示如下(略有修改):

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

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

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

因此,Lisp/Elisp中似乎有隐含的指针,但没有人愿意讲述它们。)

您的朋友是函数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

EDIT:注意list相当于几个cons调用:

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

无论何时调用conslist,都会得到而不是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))是有用的,其中E是相等谓词(eqequal)并且F是访问器(例如,carcdr)。

PPS。equal:的情况正好相反

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

暗示(实际上相当于)(equal x y);而CCD_ 37的不是

添加到@sds的答案中,因为他没有明确提及,而您询问了这个问题:

请参阅Elisp手册,节点Modifying Lists及其子节点。您询问的示例在节点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和其他列表结构修改功能的。但是Elisp手册中关于cons-cells的介绍提供了您通常要寻找的答案,此外还有关于如何在Lisp中传递参数的评论和答案。

这里还有其他一些答案可以更详细地解释这一点,但也可以用一个非常简单的类比来解释。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单元格与eq单元格的相等性,但这不是关于共享结构的一般答案,因为您无法知道其他有对象引用。通常,您将遵循一条规则,即"不要修改您没有创建的结构,除非您在文档中明确提到它,否则返回重要值。"因此,reverse不修改其参数,但允许nreverse修改(但仍然返回反转的列表;nreverse不保证列表在适当的位置反转)。如果您正在建立一个函数本地的列表,那么可以使用nreverse来反转它,因为您知道没有其他人引用它。

最新更新