【问题标题】:Why can CLOS slots be unbound?为什么 CLOS slot 可以解绑?
【发布时间】:2014-08-12 11:07:05
【问题描述】:

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

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

【问题讨论】:

  • “据说”。哪里说的? “希望它们无论如何都会被绑定到 nil ” - 你为什么希望得到一些东西,而 Common Lisp 规范却说一些不同的东西?在处理编程语言时,“希望”并不是一个真正有用的工具。例如,拥有未绑定的插槽可以检测它们是否已初始化。
  • @RainerJoswig,您对“希望”的看法是完全正确的 ;-) 在阅读任何文档之前,这只是对语言功能的一些实际探索。
  • 不仅仅是槽和特殊变量,符号也可以作为函数解绑。请参阅fmakunboundmakunbound。请注意,符号也不必具有特殊声明即可具有符号值。

标签: lisp common-lisp slot clos


【解决方案1】:

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))

我们制作了一个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

【讨论】:

    【解决方案2】:

    对于像按需计算槽值这样的事情非常方便,其中值有时可能为零。在访问未绑定的插槽时,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
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2019-02-05
      • 2010-12-21
      • 2016-11-27
      • 2019-12-22
      • 2022-11-02
      • 2014-06-09
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多