#lisp #common-lisp #slot #clos
#lisp #common-lisp #слот #clos
Вопрос:
Говорят, что только специальные переменные в Common Lisp могут быть не привязаны. Для всех лексических переменных значение по умолчанию равно nil
. Я думал, что слоты классов существуют в чем-то вроде closure, но, очевидно, это не так.
Если я определю слоты CLOS без :initform
параметра (в надежде, что они все равно будут привязаны nil
) и не буду указывать значения при создании экземпляра, я получу экземпляр с несвязанными слотами. Почему это так? Это не удобно.
Комментарии:
1. «Это сказано». Где это сказано? «в надежде, что они все равно будут привязаны к нулю» — почему вы должны на что-то надеяться, в то время как спецификация Common Lisp говорит что-то другое? «надежда» на самом деле не является полезным инструментом при работе с языками программирования. Наличие несвязанных слотов позволяет, например, определить, инициализированы они или нет.
2. @RainerJoswig, Ты совершенно прав насчет «надежды» 😉 Это было просто небольшое практическое изучение возможностей языка, прежде чем читать какие-либо документы.
3. Видишь: gigamonkeys.com/book/object-reorientation-classes.html
4. Не только слоты и специальные переменные, но и символы могут быть не привязаны как функции. См . fmakunbound и makunbound . Обратите внимание, что символ не обязательно должен иметь специальное объявление, чтобы иметь значение символа.
Ответ №1:
Это очень удобно для таких вещей, как вычисление значений слотов по требованию, где значение иногда может быть равно нулю. При доступе к несвязанному слоту CLOS обычно вызывает SLOT-UNBOUND
универсальную функцию, которая сигнализирует об ошибке. Однако вместо ошибки вы можете специализироваться SLOT-UNBOUND
на вычислении и сохранении значения по требованию. Последующие обращения будут использовать значение слота напрямую, и вы можете очистить «кэш» слота с помощью SLOT-MAKUNBOUND
.
Вы могли бы сделать это с помощью какого-либо «не привязанного» значения sentinel, но вместо этого очень удобно иметь встроенную функциональность.
Пример slot-unbound
использования:
(defclass foo ()
((bar :accessor bar)
(baz :accessor baz)))
(defmethod slot-unbound (class (instance foo) slot-name)
(declare (ignorable class))
(setf (slot-value instance slot-name) nil))
В действии:
CL-USER> (defparameter *foo* (make-instance 'foo))
*FOO*
CL-USER> (bar *foo*)
NIL
CL-USER> (setf (baz *foo*) (not (baz *foo*)))
T
Ответ №2:
Экземпляры CLOS не являются замыканиями
Экземпляры CLOS обычно не реализуются как замыкания. Это было бы сложно. Они представляют собой некоторую структуру данных с чем-то вроде вектора для слотов. Аналогично структурам Common Lisp. Разница между экземплярами CLOS и структурами усложняет это: экземпляры CLOS могут изменять количество слотов во время выполнения, и можно изменить класс экземпляра CLOS во время выполнения.
Убедитесь, что слоты имеют NIL
значение
С помощью некоторых продвинутых CLOS вы можете убедиться, что слоты имеют нулевое значение. Обратите внимание, что функции в пакете CLOS
в моем примере могут находиться в другом пакете в вашей CL.
Эта функция просматривает все слоты экземпляра. Если слот не привязан, для него устанавливается значение NIL
.
(defun set-all-unbound-slots (instance amp;optional (value nil))
(let ((class (class-of instance)))
(clos:finalize-inheritance class)
(loop for slot in (clos:class-slots class)
for name = (clos:slot-definition-name slot)
unless (slot-boundp instance name)
do (setf (slot-value instance name) value))
instance))
Мы создаем класс mixin:
(defclass set-unbound-slots-mixin () ())
Исправление будет запущено после инициализации объекта.
(defmethod initialize-instance :after ((i set-unbound-slots-mixin) amp;rest initargs)
(set-all-unbound-slots i nil))
Пример:
(defclass c1 (set-unbound-slots-mixin)
((a :initform 'something)
b
c))
CL-USER 1 > (describe (make-instance 'c1))
#<C1 4020092AEB> is a C1
A SOMETHING
B NIL
C NIL