首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >在Common Lisp对象系统中分离初始化参数和类槽来创建对象

在Common Lisp对象系统中分离初始化参数和类槽来创建对象
EN

Stack Overflow用户
提问于 2021-11-03 15:46:26
回答 1查看 104关注 0票数 3

This询问有关从其他插槽初始化插槽的信息。相反,我想要实现的是将一些参数作为输入-可能但不一定是make-instance -并将这些参数转换到类插槽中进行存储。实际上,我希望将类的实现与它的(初始化)接口分开。

有没有推荐的方法来实现这一点?

我能想到的最简单的方法就是简单地创建一个(defun make-my-object ...)作为接口。然后,这可能会使用适当的参数调用make-instance

例如,想象一下

代码语言:javascript
复制
(defclass my-object () (slot-1 slot-2))
(defun make-my-object (arg-1 arg-2)
  (make-instance 'my-object 
                 :slot-1 (+ arg-1 arg-2) 
                 :slot-2 (- arg-1 arg-2)))

我能想到的其他方法包括实现一个initialize-instance :after,它将arg-1arg-2作为关键字参数,并适当地初始化slot-1slot-2。然而,由于after方法是以最不特定的第一顺序调用的,这意味着超类插槽将在当前类插槽之前初始化。另一方面,看起来更常见的是,人们将接受构造当前类的参数,并且基于这些参数,将初始化超类插槽。

另一种选择是initialize-instance :before --或者也是:around --但是如果层次结构中的多个类有这样的“接口实现”差异,除非我可以将参数传递给call-next-method,否则我不认为这是可行的。

还有别的办法吗?

编辑:感谢@ignis volens让我注意到我主要关心的是超类插槽从子类插槽初始化的问题。有没有推荐的方法来做这件事?

EN

回答 1

Stack Overflow用户

发布于 2021-11-03 17:17:16

我不太明白你的问题。我想,答案几乎肯定是在initialize-instance上的方法之后。您说这将导致超类中定义的插槽首先被初始化:是的,它会的,这几乎肯定是您想要发生的事情。在超类中定义的插槽通常不依赖于它们在子类插槽上的值(总是可以认为所有事情都有异常),因此以最不特定的第一顺序进行初始化几乎总是你想要的。

我使用的初始化插槽的两种常见方法是在定义中简单地声明它们的初始参数:

代码语言:javascript
复制
(defclass minibeast ()
  ((legs :initform 'uncountable
         :initarg :legs
         :initarg :leg-count
         :accessor legs)
   (tentacles :initform 'many
              :initarg :tentacles
              :initarg :number-of-tentacles
              :accessor tentacles)))

现在(make-instance 'minibeast :legs 87)实现了您所期望的功能。这是可行的(因为,如果两个插槽是在不同的类中定义的,那么显然必须这样做):

代码语言:javascript
复制
(defclass awful-monster ()
  ((legs :initform 'uncountable
         :initarg :legs
         :initarg :leg-count
         :accessor legs)
   (appendages :initform 'many
               :initarg :legs
               :initarg :appendages)))

现在(make-instance 'awful-monster :legs 93)将产生一个有93条腿和93个附属物的可怕的怪物。

然而,该方法可能不符合将接口与实现分离的资格。您可能还希望在初始化插槽时执行一些计算。在这两种情况下,initialize-instance上的after方法通常都是正确的方法:

代码语言:javascript
复制
(defclass horrible-monster ()
  ((legs :initform 983
         :accessor legs)
   (eyes :initform 63
         :accessor eyes)
   (appendages
    :reader appendages)))
   
(defmethod initialize-instance :after
  ((m horrible-monster) &key eyes legs (stalky-eyes t))
  (with-slots ((e eyes) (l legs) appendages) m
    (when eyes (setf e eyes))
    (when legs (setf l legs))
    (setf appendages (if stalky-eyes (+ e l) l))))

现在可怕的怪物将得到适当数量的附属物(我不确定为什么可怕的怪物不知道他们的眼睛是否在茎上:也许它们没有镜子)。

当然,还有许多其他的组合。您可能不希望让用户代码显式调用make-instance,而是将其封装在某个函数中:

代码语言:javascript
复制
(defun make-awful-thing (&rest args &key (sort-of-horrible-thing 'horrible-monster)
                               &allow-other-keys)
  (let ((the-remaining-args (copy-list args)))
    ;; No doubt alexandria or something has a way of doing this
    (remf the-remaining-args ':sort-of-horrible0thing)
    (apply #'make-instance sort-of-horrible-thing the-remaining-args)))

当然,现在你可以很容易地使用一些定制的初始化协议:

代码语言:javascript
复制
(defgeneric enliven-horrible-thing (horrible-thing &key)
  (:method :around ((horrible-thing t) &key)
   (call-next-method)
   t))

(defun make-awful-thing (&rest args &key (sort-of-horrible-thing 'horrible-monster)
                               &allow-other-keys)
  (let ((the-remaining-args (copy-list args)))
    ;; No doubt alexandria or something has a way of doing this
    (remf the-remaining-args ':sort-of-horrible0thing)
    (apply #'enliven-horrible-thing
           (apply #'make-instance sort-of-horrible-thing
                  the-remaining-args)
           the-remaining-args)))

(defmethod enliven-horrible-thing ((horrible-thing horrible-monster)
                                   &key (ichor t) (smell 'unspeakable))
  ...)
票数 4
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/69827814

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档