汽车返回值?或者计划很奇怪?

时间:2009-04-21 01:59:37

标签: scheme

我在使用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
  3. 有什么想法吗?这是一个简单的修复,但我更好奇为什么变量的奇怪性正在发生。

4 个答案:

答案 0 :(得分: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)

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

Expression tree

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

  1. 原子(示例中的数字或图表中的符号,例如lets5sqrt
  2. 参考(在图中用箭头表示)
  3. Null(在图中表示为黑框)
  4. 您可以将其中任何一个放入任何插槽中。因此,在上面的解释中,每个#项都是一个箭头,每个非#数字都是一个原子,每个()都是一个黑盒子。所以,在行

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

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

    希望这更有意义。 : - )

答案 1 :(得分:4)

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

答案 2 :(得分:2)

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

另外,你正在考虑指针和引用,而不是考虑值,它可以是不可变值(如数字或符号)或可变值(如conses或字符串)可分配的值,垃圾收集和通过引用传递。

要记住的最后一点: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拥有一个全新的缺点,其汽车价值与x的cdr相同;这是一个利弊细胞。 与(cdr x)完全相同的缺点单元格。它由两个列表共享。当(set-cdr! (cdr x) ...)修改共享的cons单元格时,两个列表都会受到影响。

答案 3 :(得分: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 . ())

enter image description here