2

在我使用 Scheme 和列表的一个项目中,我注意到了这种半奇怪的行为。我设法将行为隔离到一个部分中。代码是:

(define x (list 1 2 3))
(define y (list 4 5))
(define z (cons (car x) (cdr y)))
(define w (append y z))
(define v (cons (cdr x) (cdr y)))
(set-car! x 6)
(set-car! y 7)
(set-cdr! (cdr x) (list 8))

x
y
z
w
v

为我们提供以下输出:

(6 2 8)
(7 5)
(1 5)
(4 5 1 5)
((2 8) 5)

谁能给我解释一下:

  1. 为什么不(set-car! x 6)更新Z?因为根据我的理解car/cdr返回指针或者引用对应的值。这真的很奇怪,我有点困惑。
  2. 如果car/cdr不返回引用/指针,那么最终如何set-cdr!操作列表v

有任何想法吗?这是一个简单的解决方法,但我更好奇为什么会出现变量的怪异现象。

4

4 回答 4

22

好的,让我们逐行执行您的程序。我还为每个新创建的对象分配了唯一的编号(如果您习惯于使用类 C 语言,可以将它们视为对象地址),以便您可以看到是什么。:-)

(define x (list 1 2 3))             ; => #1 = (1 . #2), #2 = (2 . #3), #3 = (3 . ())
(define y (list 4 5))               ; => #4 = (4 . #5), #5 = (5 . ())
(define z (cons (car x) (cdr y)))   ; => #6 = (1 . #5)
(define w (append y z))             ; => #7 = (4 . #8), #8 = (5 . #6)
(define v (cons (cdr x) (cdr y)))   ; => #9 = (#2 . #5)
(set-car! x 6)                      ; => #1 = (6 . #2)
(set-car! y 7)                      ; => #4 = (7 . #5)
(set-cdr! (cdr x) (list 8))         ; => #2 = (2 . #10), #10 = (8 . ())

现在,让我们看看您的值(对于每个引用,使用最后分配的值):

x   ; #1 => (6 . #2) => (6 . (2 . #10)) => (6 2 8)
y   ; #4 => (7 . #5) => (7 5)
z   ; #6 => (1 . #5) => (1 5)
w   ; #7 => (4 . #8) => (4 . (5 . #6)) => (4 . (5 . (1 . #5))) => (4 5 1 5)
v   ; #9 => (#2 . #5) => ((2 . #10) 5) => ((2 8) 5)

编辑:我正在添加一个图表来解释我的答案,因为您不能在评论中包含图表。我没有时间制作显示上述值的图表,但这希望能解释一些事情。

表达式树

每对都有两个“插槽”,carcdr,表示为上图中的左右框。如您所见,这些插槽中的每一个都具有三种可能的情况:

  1. 原子(示例中的数字,或图中的符号,例如lets5sqrt
  2. 参考(在图中用箭头表示)
  3. Null(在图中表示为黑框)

您可以将其中任何一个放入任何插槽中。所以,在我上面的解释中,每一项#都是一个箭头,每一个非#数字都是一个原子,每一个()都是一个黑盒子。所以,在行

(define v (cons (cdr x) (cdr y)))

您正在创建一对,其中左侧插槽与右侧插槽具有相同的内容x(即,指向对 2 的箭头),右侧插槽与右侧插槽具有相同的内容(指向第 5对y的箭头)。换句话说,两个盒子都v包含箭头,每个都指向不同的对。

希望这更有意义。:-)

于 2009-04-21T02:14:06.610 回答
4

(define z (cons (car x) (cdr y))) 为 z 的头部分配了一个新的 cons 单元。x 的头部与 z 的头部是不同的 cons 单元格,因此为 x 更改此 cons 单元格的内容不会更改 z。

于 2009-04-21T02:09:10.927 回答
2

请记住,每个变量不包含列表,它们只包含一个cons单元格。列表(或树)是多个 cons 单元的复合结构,其中一些可能在多个结构之间共享。

此外,您正在考虑指针和引用,而不是考虑值,它们可以是不可变值(如数字或符号)或可变值(如 conses 或字符串)可变值被分配、垃圾收集和通过引用传递.

最后一点要记住:该cons过程总是分配一个新的 cons 单元格。

之后(define z (cons (car x) (cdr y))),z 拥有一个全新的缺点,与 x 相同,与 x 相同的汽车,以及与 y 相同的 cdr。当你 时(set-car! x),你只需更改 x cons 单元格,而不是 z。

之后(define v (cons (cdr x) (cdr y))),v 持有一个全新的 cons,其 car 与 x 的 cdr 值相同;那是一个缺点细胞。 与(cdr x)相同的缺点单元。它由两个列表共享。当该共享 cons 单元格被 修改时(set-cdr! (cdr x) ...),两个列表都会受到影响。

于 2009-04-21T03:02:22.937 回答
0

方框图和指针图说明了@chris 制作的示例:

(define x (list 1 2 3))             ; => #1 = (1 . #2), #2 = (2 . #3), #3 = (3 . ())
(define y (list 4 5))               ; => #4 = (4 . #5), #5 = (5 . ())
(define z (cons (car x) (cdr y)))   ; => #6 = (1 . #5)
(define w (append y z))             ; => #7 = (4 . #8), #8 = (5 . #6)
(define v (cons (cdr x) (cdr y)))   ; => #9 = (#2 . #5)
(set-car! x 6)                      ; => #1 = (6 . #2)
(set-car! y 7)                      ; => #4 = (7 . #5)
(set-cdr! (cdr x) (list 8))         ; => #2 = (2 . #10), #10 = (8 . ())

在此处输入图像描述

于 2014-03-05T20:14:19.377 回答