Difference between '(()) and (cons null null)

2019-07-29 05:49发布

问题:

I am confused about the difference between '(()) and (cons null null) in scheme.

The code below show that b and c are completely the same thing.

(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)

However, when I used dup instead of 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)

Variable b and c are different. I have done some experiments, but I haven't understand yet.

回答1:

The value of d in the first implementation is literal data, and is modified with undefined consequences. To highlight what's happening, consider the following code:

(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))

The expected result is that (incorrect-list-null-and-x n) should return a list of the form (() n), and it does the first time, but successive calls are still accessing the same data:

(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)

The same problem manifests itself a bit differently in your dup2. Every value returned from dup2 is actually the same pair:

(let* ((x (dup2 (cons 1 2)))
       (y (dup2 (cons 3 4))))
  (display x)
  (display y))

outputs:

(3 . 4)(3 . 4)

because the call (dup2 (cons 3 4)) modifies the same structure that was previously returned by (dup2 (cons 1 2)).



回答2:

Data literals, like '(()), are meant to be read-only, and modifying it using set-car! or set-cdr! has undefined behaviour. For predictable behaviour, use the (cons '() '()) version if you want to use set-car! or set-cdr! on it.

In particular, cons creates a new cons cell, whereas a data literal usually won't.

Still, for the purposes of implementing dup, why are you using set-car! and set-cdr! anyway? Just use cons directly:

 (define (dup x)
   (cons (car x) (cdr x)))


回答3:

In your first code snippet you use (d '(())) which ends up binding a literal to d. You then modify the literal which is generally undefined. In your second code snippet you use (d (cons null null)) which binds d to a newly created 'cons cell' which you then modify. There is no problem modifying that.

Note: you've not defined null. Perhaps you meant '()?



标签: lisp scheme