define 生成一个变量。计算对象位置的名称。
(define foo (list 'a 'b))
这里的foo 可能指向地址240。在评估foo 时,您会得到240,因此(define bar foo) 将创建第二个变量,该变量指向内存中的同一对象。地址未显示在 REPL 中,因此您将看到 (a b),因为 REPL 会打印地址 240 中的内容。
内存中的这个对象是一个cons,所以每一个都只是指向内存中其他值的两个指针。因此,当你这样做时:
(set-car foo 'c)
您正在通过将car 指针从a 的地址表示更改为c 的地址表示来更改位于地址240 的缺点。由于foo 和bar 都指向地址240 的同一个cons 单元格,因此它们都将获得更改,因为它们指向相同的数据。
如果您使用set!,您只会更改名称指向的值:
(set! foo (cons 'd (cdr foo)))
现在这会将变量指针从指向240 更新到其他地址。因此,您可以评估 foo 和 bar 并查看它们是不同的值:
foo ; ==> (d b)
bar ; ==> (c b)
这里foo 可能位于地址256。由于我们在 foo 中使用了旧值 cdr,我们知道它们的尾部是相同的:
(cdr foo) ; ==> (b)
(eq? (cdr foo) (cdr bar)) ; ==> #t
如果您将set! bar 设置为不同的值:
(set! bar foo)
现在foo 和bar 都指向256,如果不再使用240,则该位置的对可能会被垃圾收集,但由于原始foo 的cdr 是cdr,假设地址248,该地址不会被垃圾回收,因为它是由地址256 中的cons 指向的。
注意:我已经更改了 foo 的定义,因为您不允许使用 set-car 像 '(a b) 这样的文字,因为它们是不可变的。 (list 'a 'b) 每次执行该行代码时都会创建一个新列表,您可以确保该地址不会在例如。 '(d a b).
NB2:Scheme 报告尽量避免规定如何存储值。因此他们没有在报告中使用概念地址。 Scheme 和 CL 的大多数实现都对它们的数据进行了非常相似的建模。