简明规范

时间:2018-09-07 00:59:51

标签: common-lisp

有关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,对吗?

我以上假设ba拥有cons对象,而不是cons的指针。 即使实际的lisp实现与指针一起工作,这也应该在repl级别不可见,还是应该按照规范显示?当假设ab都持有指向同一a的指针时,可以达到类似的效果。 b,即通过重复应用cons来构造列表,支持这样的假设:conses由符号值中的指针表示。

以下两种形式是等效的:

Consing
cons

2 个答案:

答案 0 :(得分:5)

  

设置b的汽车会更改a的汽车。

您没有设置car中的b。您正在设置carb引用的同一个con单元格的a

CL-USER 1 > (let (a b)
              (setq a (cons 1  2))
              (setq b a)

              (eq a b))
T

说明:

  • 我们有变量ab
  • (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的指针。

错了。 ab是变量,它们都指向相同的单个cons单元格。

  

“ cons构造存储两个值或指向值的指针的内存对象”

在Common Lisp实现中,一些小数字(fixnum)可能直接存储在cons单元格中。一个人不能通过身份可靠地比较数字(使用EQ),而必须进行数字比较(EQL=,...)。

OTOH,cons单元不能存储在con单元内部,因此内部由指针引用。

您使用的操作:

  • SETQ求第一个form1并将结果存储在变量var1 中。 ->注意它是怎么说的:结果而不是结果的副本

  • RPLCA-这是(setf CAR)实际使用的。 : rplaca用对象代替了cons车,并且 cons被修改->从而修改了它作为参数传递的cons对象。

  • Evaluation-由于执行代码,因此评估规则适用。

进一步了解Lisp的执行模型:

  • Lisp旧书:John Allen撰写的“ LISP解剖”。 (amazon
  • 诸如R5RS之类的计划规范。
  • Lisp in small pieces,这本书解释了Scheme / 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);