考虑缺点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
其他函数示例可以显示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 cell是共享结构的一部分",x1
和x2
的cdr被称为"共享链接",而x1
和x2
的图形显示如下(略有修改):
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中似乎有隐含的指针,但没有人愿意讲述它们。)
您的朋友是函数eq
和equal
。
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))
无论何时调用cons
或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))
是有用的,其中E
是相等谓词(eq
或equal
)并且F
是访问器(例如,car
或cdr
)。
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
单元是一个非常小的容器:它容纳两个元素,并具有用于取回这些元素的访问器car
和cdr
。
在大多数面向对象的编程语言中,当您将对象放入容器中时,不会自动复制对象。例如,在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
来反转它,因为您知道没有其他人引用它。