为什么CLOS插槽可以解除绑定?



据说,在Common Lisp中只有特殊的变量可以解绑定。对于所有词法变量,默认值都是nil。我认为类槽存在于闭包之类的东西中,但显然它们不存在。

如果我定义一个没有:initform参数的CLOS插槽(希望它们无论如何都会绑定到nil),并且在创建实例时不提供值,我会得到一个具有未绑定插槽的实例。为什么会这样呢?不方便。

对于按需计算槽值之类的事情非常方便,其中值有时可能为nil。当访问未绑定的插槽时,CLOS通常调用SLOT-UNBOUND通用函数,该函数表示错误。但是,您可以专门化SLOT-UNBOUND来根据需要计算和存储该值,而不是产生错误。后续访问将直接使用槽位值,您可以通过使用SLOT-MAKUNBOUND来刷新槽位"缓存"。

您可以使用某种类型的哨兵"未绑定"值来完成此操作,但是使用内置功能更方便。

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

CLOS实例不是闭包

CLOS实例通常不作为闭包实现。这将是困难的。它们是某种数据结构,类似于槽的向量。类似于Common Lisp的结构。CLOS实例和结构之间的差异使其复杂化:CLOS实例可以在运行时更改槽的数量,并且可以在运行时更改CLOS实例的类。

确保插槽有NIL

使用一些高级CLOS,您可以确保slot具有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))

创建一个mixin类:

(defclass set-unbound-slots-mixin () ())

修复将在初始化对象后运行。

(defmethod initialize-instance :after ((i set-unbound-slots-mixin) &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

相关内容

  • 没有找到相关文章

最新更新