2015-04-26 3 views
4

Прежде всего, позвольте мне сказать, что я новичок в Лиспе. Честно говоря, я был новичком в течение некоторого времени, но есть еще много вещей, которые я плохо знаю.В чем разница между (list nil) и '(nil) в Lisp?

Пока я писал this question, я придумал странную ошибку в своем коде.

Описание Вот функция, которая вернет список (0 1 ... n) со списком e. Он использует rplacd, чтобы отслеживать последний элемент, чтобы избежать окончательного вызова last.

Например, (foo 4 '(x))(0 1 2 3 4 x).

«Голова» хранится в a, что это не просто nil, потому что есть только один nil, и никогда его копия (если я правильно понимаю), поэтому я не могу просто добавить к nil.

(defun foo (n e) 
    (let* ((a (list nil)) (tail a)) 
     (loop for i to n 
       do (rplacd tail (setf tail (list i))) 
       finally (rplacd tail (setf tail e)) 
       (return (cdr a))))) 

(defun bar (n e) 
    (let* ((a '(nil)) (tail a)) 
     (loop for i to n 
       do (rplacd tail (setf tail (list i))) 
       finally (rplacd tail (setf tail e)) 
       (return (cdr a))))) 

Единственное различие между этими функциями является (list nil) заменен '(nil) в bar. Пока foo работает как ожидалось, bar всегда возвращает nil.

Моя первоначальная догадка заключается в том, что исходный cdr из a действительно nil, а цитируемый список может считаться постоянным. Однако, если я делаю (setf x '(nil)) (rplacd x 1), я получаю (nil . 1), как и ожидалось, поэтому я должен быть хотя бы частично ошибочным.

+0

Похоже, что скобки «bar» не сбалансированы. – user2357112

+0

@ user2357112 Исправлено. Отсутствующий закрывающий парен был сразу после ''(nil)'. Прошу прощения. –

+0

Я не знаком с Common Lisp, но я верю в схему, все, что цитируется с '' ', должно рассматриваться как неизменяемое. Это держится в Common Lisp? Похоже, вы пытаетесь изменить эту вещь. – user2357112

ответ

5

При оценке: (nil) и (list nil) производят похожие списки, но первые могут считаться постоянными, если они присутствуют в исходном коде. Вы не должны выполнять какие-либо деструктивные операции в постоянном списке в Common Lisp. См. http://l1sp.org/cl/3.2.2.3 и http://l1sp.org/cl/quote. В частности, последний говорит: «Последствия не определены, если литеральные объекты (включая цитируемые объекты) разрушительно изменены».

+0

Это имеет смысл, спасибо. Поэтому, конечно, это только благодаря * неопределенным последствиям *, если '(setf x '(nil)) (rplacd x 1)' делает то, что я ошибочно ожидал? Здесь схема более дружелюбна, так как она уведомит об ошибке, IIRW. –

+0

Когда SBCL может обнаружить изменение постоянных данных, он выдает полезное предупреждение и ссылается на применимые биты стандарта. (Вот как я посмотрел ссылки на мой ответ.) Но он пока не может обнаружить все ситуации. До тех пор вы просто должны знать правило. – Xach

+1

@ Jean-ClaudeArbaut: В старой версии часто задаваемых вопросов говорится: в разделе 3.4 R5RS указано, что ошибка заключается в попытке изменить неизменяемый объект, такой как литеральный список. Не все схемы сообщают об этом как об ошибке, но вместо этого изменяют литеральный список. –

3

Данные в кавычках считаются константой. Если у вас есть две функции:

(defun test (&optional (arg '(0))) 
    (setf (car arg) (1+ (car arg))) 
    (car arg)) 

(defun test2() 
    '(0)) 

Эти две функции как с помощью постоянного списка (0) правильно?

  1. Реализация может выбрать, чтобы не мутировать константы:

    (test) ; ==> Error, into the debugger we go 
    
  2. Реализация может cons тот же список дважды (читатель может сделать это за него)

    (test2) ; ==> (0) 
    (test) ; ==> 1 
    (test) ; ==> 2 
    (test) ; ==> 3 
    (test2) ; ==> (0) 
    
  3. The реализация может видеть, что это то же самое и экономия места:

    (test2) ; ==> (0) 
    (test) ; ==> 1 
    (test) ; ==> 2 
    (test) ; ==> 3 
    (test2) ; ==> (3) 
    

Фактически. Последние два поведения могут происходить в одной и той же реализации, зависящей от скомпилированной функции или нет.

В CLISP обе функции работают одинаково.Я также вижу при дизассемблировании с SBCL, что константа на самом деле мутирована, поэтому я задаюсь вопросом, возможно ли, что она имеет постоянную сволочку (cdr '(0)) во время компиляции и вообще не использует измененный список. Это действительно не имеет значения, поскольку оба они считаются хорошим «неопределенным» поведением.

Часть from CLHS об этом очень мало

Последствие не определенно, если буквальные объекты (в том числе цитируемого объектов) деструктивно изменены.

+0

Спасибо. Чтобы быть уверенным, что если я ** copy-list ** цитируемый список, копия изменена, не так ли? –

+1

@ Jean-ClaudeArbaut Да, но только верхний уровень, так как 'copy-list' делает мелкую копию. Таким образом, он не делает рекурсивные копии элементов списка в списке, например ['copy-tree'] (http://www.lispworks.com/documentation/lw61/CLHS/Body/f_cp_tre.htm#copy-tree). – Sylwester

+0

Еще один вопрос: на странице [CLHS] (http://www.lispworks.com/documentation/HyperSpec/Body/f_nconc.htm) для функции ** nconc ** есть пример: * (setq x ' (abc)) (setq y '(def)) (nconc xy) * Разве это не так? Я имею в виду, что и x, и y содержат литерный список, а x изменен nconc. –

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