2013-10-12 1 views
2

Я немного запутался в разрушительной DELETE функции Common Lisp. Это, кажется, работает, как и следовало ожидать, ибо если элемент является первым элементом в списке, за исключением:DELETE является разрушительным - но не всегда?

CL-USER> (defvar *test* (list 1 2 3)) 
*TEST* 
CL-USER> (delete 1 *test*) 
(2 3) 
CL-USER> *test* 
(1 2 3) 
CL-USER> (delete 2 *test*) 
(1 3) 
CL-USER> *test* 
(1 3) 

ответ

5

«Убойная» не означает, что «работает на месте». Это означает, что структура действующего значения может быть изменена каким-то произвольным и часто неопределенным способом. Это может быть в некоторых случаях, реализации и случаи имеют эффект, как если бы он был «на месте». Однако это, как правило, нельзя полагаться.

Если вы используете деструктивный оператор, вы сообщаете компилятору, что вы не заинтересованы в предыдущем значении переменной после завершения операции.Вы должны предположить, что это значение искажается до неузнаваемости после этого и больше не использует его. Вместо этого вы должны использовать возвращаемое значение операции.

(let ((a (list 1 2 3))) 
    (let ((b (delete 2 a))) 
    (frob b)) 
    a) 

=> You were eaten by a grue. 

Если вы не уверены в безопасности деструктивных операций, использовать их неразрушающие аналоги (remove в данном случае).

(let ((a (list 1 2 3))) 
    (let ((b (remove 2 a))) 
    (frob b)) 
    a) 

=> (1 2 3) 

Если вы действительно хотите изменить содержимое переменной, установите их возвращаемого значения операции:

(let ((a (list 1 2 3))) 
    (setf a (delete 2 a)) 
    a) 

=> (1 3) 
+2

Ну, не столько «предыдущее значение переменной» как «структура значения, которое имеет по крайней мере эта переменная». В конце концов, структура распределения не является чем-то необычным. – Vatine

+1

И это не ограничивается «значением переменной [a]». Вы можете сделать, например, '(delete 1 (rest some-list))'. Ему просто нужен список в качестве аргумента; это не макрос и не нуждается в _place_. –

+0

@mck Обратите внимание, что результаты в первом случае не так непредсказуемы, как «вы были съедены грубой». В то время как автомобиль и cdr любой ячейки cons в списке могут быть изменены, они все равно будут cons-ячейками. В первом случае 'a' всегда будет ячейкой cons после вызова' (delete 2 a) ', даже если после этого автомобиль и cdr будут разными. Еще важнее то, что это все равно будет той же самой, что и раньше. Например, см. [Этот образец кода] (http://pastebin.com/qVUfHeV8). –

2

DELETE работа путем изменения CDR в предыдущих минусах ячейки списка, чтобы указать на один мимо элемент (s) удаляется. Но если вы удаляете первый элемент списка, для изменения нет предыдущей ячейки cons.

Хотя эта реализация на самом деле не указана стандартом языка, это практически практически каждая реализация.

Поскольку для удаления первого элемента списка не существует предыдущей ячейки cons, она просто возвращает вторую ячейку cons cons. Поэтому даже если DELETE разрешено изменять список, вы все равно должны назначить результат своей переменной для обработки этого случая. Кроме того, следует подчеркнуть, что деструктивным поведением является только разрешено по стандарту, а не требуется. Таким образом, есть отдаленная возможность того, что некоторая реализация может вообще не реализовать ее разрушительно, и вы также должны это учитывать.

И даже если DELETE работал с изменением CAR, а не с CDR, все еще существует случай, когда он не может разрушить: удаление всех элементов списка, например.

(setq *test* (list 'a 'a)) 
(delete 'a *test*) 

Это приводит к пустой список, т.е. NIL. Но *test* все еще содержит ячейку cons, которая была главой исходного списка, и нет никакого способа для DELETE, чтобы изменить это. Так что вы должны сделать:

(setq *test* (delete 'a *test*)) 

установить *test* в NIL.

+1

Это может быть общей реализации, но это не указано. «Удалить» может собирать все неиспользуемые элементы и возвращать новый список (т. Е. Быть полностью неразрушающим) или может копировать _n_ незатушенные элементы в 'car' из первых _n_ cons ячеек в списке , и установите для _cdr_ ячейки _n_ значение 'nil'. Конкретная реализация не продиктована стандартом. Ваше описание также не будет охватывать случай, например, '(delete 1 (list 1))' и '(delete 1 (list 1 2))', где ничего не нужно изменять; 'delete' может просто вернуть' cdr' аргумента списка. –

8

Имейте в виду, что DELETE должен работать над списком, а не по переменной. DELETE получает переданный список (указатель на первые минусы), а не переменную.

С delete не имеет доступа к переменной *test*, она не может ее изменить. «Это» означает его привязки. *test* будет указывать на те же ячейки cons, что и раньше. Единственное, что можно изменить, это содержимое ячейки cons или содержимого других cons-ячеек, на которые указывает первая cons-ячейка.

Одно можно сомневаться, независимо от того, что DELETE., *test* всегда будет указывать на одну и ту же камеру.

Что следует из этого? Если вы хотите иметь *test* точку в результате операции удаления, то вы явно должны сказать так:

(setq *test* (delete 1 *test*))