2013-10-03 3 views
1

Я смущен о различии между' (()) и (минус null null) в схеме.Разница между '(()) и (минус 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 различны. Я сделал несколько экспериментов, но пока не понял.

+1

Возможный дубликат [Неожиданное сохранение данных] (http: // stackoverflow.ком/вопросы/18790192/неожиданная-живучесть-в-данных). Проблема связана с цитируемыми данными не из-за разницы в значениях '' (()) 'и' (cons null null) '. Попробуйте заменить '' (()) 'на' (list '()) ', и вы больше не будете наблюдать эту проблему. (Этот дубликат - это не схема, а Common Lisp, но проблема одна и та же.) Для еще большей забавы, а не для процедуры 'dup', попробуйте написать одну, которая _pushes_ данных в список' d', Посмотрите, как список становится длиннее и длиннее для нескольких вызовов. –

ответ

0

Литералы данных, такие как '(()), предназначены только для чтения и могут быть изменены с использованием set-car! или set-cdr! имеет неопределенное поведение. Для предсказуемого поведения используйте версию (cons '() '()), если вы хотите использовать set-car! или set-cdr!.

В частности, cons создает новую ячейку cons cons, тогда как литерал данных обычно не будет.

Тем не менее, для целей осуществления dup, почему являются вы используете set-car! и set-cdr! в любом случае? Просто используйте cons непосредственно:

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

В своем первом фрагменте кода вы используете (d '(())) который заканчивается обязательным буквальным к d. Затем вы изменяете литерал, который обычно не определен. Во втором фрагменте кода вы используете (d (cons null null)), который связывает d с вновь созданной ячейкой cons, которую вы затем модифицируете. Нет проблем с этим.

Примечание: вы не указали null. Возможно, вы имели в виду «()?

+0

null определяется в схеме как '() Редактирование: исправление только в некоторых реализациях спасибо – DanielV

+0

'(define null' null)' не является хорошим предположением. OP явно намеревается '' (()) 'быть' equal? ​​'To' (cons null null) '. –

+1

@ DanielV Только в некоторых реализациях. Он не определен в стандартах Схемы. –

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

Смежные вопросы