Common Lisp: как заключать скобки в SBCL

В Common Lisp специальный оператор quote делает все, за чем следует невычисленное, например

(quote a) -> a
(quote {}) -> {}

Но почему форма (quote()) дает мне ноль? Я использую SBCL 1.2.6, и вот что я получил в REPL:

CL-USER> (quote ())
NIL

Подробнее об этой проблеме: это код из главы 24 PCL.

(defun as-keyword (sym)
  (intern (string sym) :keyword))

(defun slot->defclass-slot (spec)
  (let ((name (first spec)))
    `(,name :initarg ,(as-keyword name) :accessor ,name)))

(defmacro define-binary-class (name slots)
  `(defclass ,name ()
     ,(mapcar #'slot->defclass-slot slots)))

Когда макрос расширяется для следующего кода:

(define-binary-class id3-tag
    ((major-version)))

is

(DEFCLASS ID3-TAG NIL
      ((MAJOR-VERSION :INITARG :MAJOR-VERSION :ACCESSOR MAJOR-VERSION)))

который равен NIL, а не () после имени класса ID3-TAG.


person shizhz    schedule 27.02.2015    source источник
comment
@FrédéricHamidi Спасибо :) Раньше я этого не замечал.   -  person shizhz    schedule 27.02.2015
comment
@FrédéricHamidi Не могли бы вы добавить свой комментарий в качестве ответа, пожалуйста? Так что его можно пометить как правильный ответ. Спасибо.   -  person rudolfo.christ    schedule 27.02.2015


Ответы (2)


nil и () — два способа выразить одно и то же понятие (пустой список).

Традиционно nil используется для выделения логического значения false, а не пустого списка, а () используется наоборот.

Common LISP HyperSpec говорит:

() ['ноль], н. альтернативное обозначение для написания символа nil, используемое для подчеркивания использования nil в качестве пустого списка.

person Frédéric Hamidi    schedule 27.02.2015

Ваше наблюдение связано с тем, что объект имеет более одного представления. В Common Lisp программа чтения (которая читает код и читает выражения) анализирует текст на структуру и данные. Когда это данные, модуль записи может распечатать их снова, но он не будет точно знать, как данные были представлены при первоначальном считывании. Модуль записи будет печатать один объект только одним способом, следуя значениям по умолчанию и настройкам, даже если существует несколько представлений. для этого объекта.

Как вы заметили, nil, NIL, nIL, NiL,..., 'nil, 'NIL, () и '() читаются как один и тот же объект. Я не уверен, что стандарт точно диктует, каким должно быть представление по умолчанию, поэтому я предполагаю, что некоторые реализации выбирают одно из NIL, nil или, может быть, даже ().

С cons представление зависит от того, является ли cdr cons/nil или нет:

'(a . nil)        ; ==> (a)
'(a . (b . c))    ; ==> (a b . c)
'(a . (b . nil))  ; ==> (a b)

С помощью чисел читатель может получить подсказки о том, какую базу вы используете. Если в тексте не используется база, он будет использовать любое *read-base*:

(let ((*read-base* 2)) ; read numbers as boolean
  (read-from-string "(10 #x10)")) ; ==> (2 16) 

#x говорит читателю интерпретировать остальное как шестнадцатеричное значение. Теперь, если бы ваша база печати была равна 4, ответ на приведенный выше вопрос был бы визуализирован как (2 100).

Подводя итог... Одно значение в Common Lisp может иметь несколько хороших представлений, и все они будут давать одно и то же значение. То, как печатается значение, будет зависеть как от реализации, настроек, так и от аргументов функций, которые их производят. Ни то, что он принимает в качестве значения, ни различные способы, которыми он может визуализировать значение, ничего не говорят о том, как значение на самом деле хранится внутри.

person Sylwester    schedule 27.02.2015
comment
Спасибо :) Кстати, на данный момент, если бы ваша база печати была равна 4, ответ на приведенный выше вопрос был бы визуализирован как (2 100). Вы имеете в виду установить print-base на 4? Потому что я пробовал (let ((*read-base* 2) (*print-base* 4)) (read-from-string (10 #x10))), но результат все равно (2 16) - person shizhz; 27.02.2015
comment
@seki-shi Если бы вы использовали print для печати результата внутри let, вы бы напечатали (2 100), а затем вернули (2 16), поскольку *print-base* было другим. Чтобы заставить REPL печатать в базе 4, вам нужно использовать setf/setq, поскольку REPL отображает результат, который является результатом let после того, как локальные привязки исчезли. например (setf *print-base* 4) - person Sylwester; 27.02.2015