为什么CLOS插槽可以不绑定?

Mar*_*ark 0 lisp common-lisp clos slot

据说,只有Common Lisp中的特殊变量才能解除绑定.对于所有词汇变量,默认值为nil.我认为类槽存在于封闭之类的东西中,但显然它们不存在.

如果我定义一个没有:initform参数的CLOS插槽(希望它们将被绑定nil)并且在创建实例时不提供值,我将获得一个带有未绑定插槽的实例.为什么会这样?这不方便.

Xac*_*ach 7

这对于按需计算槽值的方式非常方便,其中值有时可能为零.当访问未绑定的插槽时,CLOS通常会调用SLOT-UNBOUND泛型函数,该函数会发出错误信号.但是,您可以专门SLOT-UNBOUND根据需要计算和存储值,而不是出错.后续访问将直接使用插槽值,您可以使用刷新插槽"缓存" SLOT-MAKUNBOUND.

你可以使用某种类型的sentinel"not-bound"值来做到这一点,但是内置这些功能非常方便.

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))
Run Code Online (Sandbox Code Playgroud)

在行动:

CL-USER> (defparameter *foo* (make-instance 'foo))
*FOO*
CL-USER> (bar *foo*)
NIL
CL-USER> (setf (baz *foo*) (not (baz *foo*)))
T
Run Code Online (Sandbox Code Playgroud)


Rai*_*wig 5

CLOS实例不是闭包

CLOS实例通常不实现为闭包.那很难.它们是一些数据结构,类似于插槽的向量.与Common Lisp的结构类似.CLOS实例和结构之间的区别使它复杂化:CLOS实例有可能在运行时更改槽的数量,并且可以在运行时更改CLOS实例的类.

确保插槽有NIL

使用一些高级CLOS,您可以确保插槽具有NIL值.请注意,CLOS我的示例中的包中的函数可能位于CL中的另一个包中.

此函数查看实例的所有插槽.如果插槽未绑定,则将其设置为NIL.

(defun set-all-unbound-slots (instance &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))
Run Code Online (Sandbox Code Playgroud)

我们制作一个mixin类:

(defclass set-unbound-slots-mixin () ())
Run Code Online (Sandbox Code Playgroud)

初始化对象后将运行此修复程序.

(defmethod initialize-instance :after ((i set-unbound-slots-mixin) &rest initargs)
  (set-all-unbound-slots i nil))
Run Code Online (Sandbox Code Playgroud)

示例:

(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
Run Code Online (Sandbox Code Playgroud)