有关car and cdr的维基百科页面上说cons
是一对指针。
以下代码似乎证实了这一点:
(progn
(setq a '(1 . 2))
(setq b a)
(setf (car b) 10)
(print a))
对该表格的求值给出了弊端(10 . 2)
。设置car
的{{1}}会更改b
的{{1}}。您可以在online repl at compileonline.com中进行尝试。
Common Lisp specification中定义的行为在哪里?
(我已经阅读了一些文字,但找不到指向该行为的部分。)
有趣的是,维基百科页面说:“ car
构造了存储两个值或指向值的指针的内存对象”。如果将原子1直接存储在cons对象中,那么更改a
不会更改cons
,对吗?
我以上假设b
和a
拥有cons对象,而不是cons的指针。
即使实际的lisp实现与指针一起工作,这也应该在repl级别不可见,还是应该按照规范显示?当假设a
和b
都持有指向同一a
的指针时,可以达到类似的效果。
b
,即通过重复应用cons
来构造列表,支持这样的假设:conses由符号值中的指针表示。
以下两种形式是等效的:
Consing
cons
答案 0 :(得分:5)
设置b的汽车会更改a的汽车。
您没有设置car
中的b
。您正在设置car
和b
引用的同一个con单元格的a
。
CL-USER 1 > (let (a b)
(setq a (cons 1 2))
(setq b a)
(eq a b))
T
说明:
a
和b
。(cons 1 2)
返回一个cons单元格(setq a (cons 1 2))
将con设置为(cons 1 2)
的结果,即一个cons单元格。(setq b a)
求值a
,该值返回cons单元格上方,并将b
设置为该cons单元格。要理解的关键是对变量和函数的求值返回非原始对象(即原始数字,字符等),而不是副本。
我在上面假设a和b包含cons对象,而不是cons的指针。
错了。 a
和b
是变量,它们都指向相同的单个cons单元格。
“ cons构造存储两个值或指向值的指针的内存对象”
在Common Lisp实现中,一些小数字(fixnum)可能直接存储在cons单元格中。一个人不能通过身份可靠地比较数字(使用EQ
),而必须进行数字比较(EQL
,=
,...)。
OTOH,cons单元不能存储在con单元内部,因此内部由指针引用。
您使用的操作:
SETQ:求第一个form1并将结果存储在变量var1 中。 ->注意它是怎么说的:结果而不是结果的副本。
RPLCA-这是(setf CAR)
实际使用的。 : rplaca用对象代替了cons车,并且 cons被修改->从而修改了它作为参数传递的cons对象。
Evaluation-由于执行代码,因此评估规则适用。
进一步了解Lisp的执行模型:
答案 1 :(得分:4)
首先,您的代码无效,因为不允许您修改常量列表。应该是:
(progn
(setq a (cons 1 2))
(setq b a)
(setf (car b) 10)
(print a))
当执行诸如(setq b a)
之类的分配时,它将b
的值设置为与a
的值相同。 SETQ
的规范并没有说明如何复制值,因此这两个变量包含 same 值,在这种情况下,它是一个cons单元格。
设置该cons单元格的汽车会修改该对象-再次,没有完成复制。因此,您将通过引用cons单元格的任何变量或任何其他引用(可能在结构插槽,数组元素,另一个cons单元格等中)看到更改。
我认为规范实际上并没有说出来,这些东西都是一样的,这只是隐含在我们传递抽象对象这一事实上,除非您调用一个明确定义为此(例如COPY-TREE
)。
该规范没有讨论指针,但是通常是在幕后进行的。缺点单元格就像一个C结构:
typedef struct cons {
lisp_object car,
lisp_object cdr
} cons;
lisp_object
可能是各种类型的联合(诸如FIXNUM
之类的某些立即类型,以及其他类型的指针)。当变量包含cons
时,它实际上包含指向上述结构的指针,并且赋值复制该指针,而不是该结构。所以Lisp代码类似于C代码,例如:
cons *a = make_cons(1, 2);
cons *b = a;
b->car = 10;
printf("%d\n", a->car);