我对方案中'(())和(cons null null)之间的区别感到困惑。
以下代码显示b
和c
完全相同。
(define (dup2 x)
(let ((d '(())))
(set-car! d (car x))
(set-cdr! d (cdr x))
d))
(define a '(1 2))
(define b (dup2 a))
(define c (dup2 a))
(set-car! b 2)
> c ;; --> (2 2)
但是,当我使用dup
代替dup2
时:
(define (dup x)
(let ((d (cons null null)))
(set-car! d (car x))
(set-cdr! d (cdr x))
d))
(define a '(1 2))
(define b (dup a))
(define c (dup a))
(set-car! b 2)
> c ;; --> (1 2)
变量b
和c
不同。我做了一些实验,但我还不明白。
答案 0 :(得分:1)
第一个实现中d
的值是文字数据,并且使用未定义的结果进行修改。要突出显示正在发生的事情,请考虑以下代码:
(define (incorrect-list-null-and-x x)
(let ((l '(()))) ; a list of the form (() . ())
(set-cdr! l (cons x (cdr l))) ; (cdr l) is (), so (cons x (cdr l)) should be (x . ()) == (x), right?
; and now l should be (() . (x . ())) == (() x), right?
l))
预期的结果是(incorrect-list-null-and-x n)
应返回(() n)
形式的列表,并且它是第一次执行,但后续调用仍在访问相同的数据:
(incorrect-list-null-and-x 1) ;=> (() 1)
(incorrect-list-null-and-x 2) ;=> (() 2 1)
(incorrect-list-null-and-x 3) ;=> (() 3 2 1)
(incorrect-list-null-and-x 4) ;=> (() 4 3 2 1)
同样的问题在dup2
中表现得有点不同。从dup2
返回的每个值实际上都是相同的对:
(let* ((x (dup2 (cons 1 2)))
(y (dup2 (cons 3 4))))
(display x)
(display y))
输出:
(3 . 4)(3 . 4)
因为调用(dup2 (cons 3 4))
修改了(dup2 (cons 1 2))
之前返回的相同结构。
答案 1 :(得分:0)
数据文字(如'(())
)应该是只读的,使用set-car!
或set-cdr!
修改它的行为是未定义的。对于可预测的行为,如果要在其上使用(cons '() '())
或set-car!
,请使用set-cdr!
版本。
特别是,cons
创建一个新的cons单元格,而数据文字通常不会。
但是,为了实现dup
,为什么 你仍在使用set-car!
和set-cdr!
?只需直接使用cons
:
(define (dup x)
(cons (car x) (cdr x)))
答案 2 :(得分:0)
在您的第一个代码段中,您使用(d '(()))
,最终将文字绑定到d
。然后修改通常未定义的文字。在您的第二个代码段中,您使用(d (cons null null))
将d
绑定到新创建的'cons cell',然后您将其修改。修改它没有问题。
注意:您尚未定义null
。也许你的意思是'()?