是否有克隆CLOS对象的通用方法?

时间:2012-06-16 23:47:38

标签: object clone common-lisp clos

我正在寻找一种以浅层方式克隆CLOS对象的方法,因此创建的对象将是每个插槽中具有相同值的相同类型,但是是一个新实例。我发现最接近的是标准函数复制结构,它为结构执行此操作。

4 个答案:

答案 0 :(得分:11)

通常没有标准的预定义方法来复制CLOS对象。如果可能的话,提供合理的默认复制操作并不是微不足道的,它可以为大多数时间(至少)对任意对象做正确的事情,因为正确的语义从一个类变为另一个类,从应用程序变为应用程序。 MOP提供的扩展可能性使得提供这样的默认更加困难。而且,在CL中,作为垃圾收集语言,实际上并不经常需要复制对象,例如,作为参数传递或返回时。因此,根据需要实施复制操作可能是最干净的解决方案。

话虽如此,这是我在我的一个片段文件中找到的,它可能会做你想要的:

(defun shallow-copy-object (original)
  (let* ((class (class-of original))
         (copy (allocate-instance class)))
    (dolist (slot (mapcar #'slot-definition-name (class-slots class)))
      (when (slot-boundp original slot)
        (setf (slot-value copy slot)
              (slot-value original slot))))
    copy))

您需要为class-slotsslot-definition-name提供一些MOP支持。

(我可能是从an old c.l.l thread采用了这个,但我无法记住。我从来没有真正需要这样的东西,所以它完全未经测试。)

你可以像这样使用它(用CCL测试):

CL-USER> (defclass foo ()
           ((x :accessor x :initarg :x)
            (y :accessor y :initarg :y)))
#<STANDARD-CLASS FOO>
CL-USER> (defmethod print-object ((obj foo) stream)
           (print-unreadable-object (obj stream :identity t :type t)
             (format stream ":x ~a :y ~a" (x obj) (y obj))))
#<STANDARD-METHOD PRINT-OBJECT (FOO T)>
CL-USER> (defparameter *f* (make-instance 'foo :x 1 :y 2))
*F*
CL-USER> *f*
#<FOO :x 1 :y 2 #xC7E5156>
CL-USER> (shallow-copy-object *f*)
#<FOO :x 1 :y 2 #xC850306>

答案 1 :(得分:6)

这是danlei提交的功能略有不同的版本。我刚刚写了这篇文章,偶然发现了这篇文章。由于我不完全回想起的原因,复制后会调用REINITIALIZE-INSTANCE。我认为它可以通过将额外的initargs传递给这个函数来对新对象进行一些更改

e.g。

(copy-instance *my-account* :balance 100.23)

这也被定义为对象是标准对象的泛型函数。这可能是也可能不是正确的事情。

(defgeneric copy-instance (object &rest initargs &key &allow-other-keys)
  (:documentation "Makes and returns a shallow copy of OBJECT.

  An uninitialized object of the same class as OBJECT is allocated by
  calling ALLOCATE-INSTANCE.  For all slots returned by
  CLASS-SLOTS, the returned object has the
  same slot values and slot-unbound status as OBJECT.

  REINITIALIZE-INSTANCE is called to update the copy with INITARGS.")
  (:method ((object standard-object) &rest initargs &key &allow-other-keys)
    (let* ((class (class-of object))
           (copy (allocate-instance class)))
      (dolist (slot-name (mapcar #'sb-mop:slot-definition-name (sb-mop:class-slots class)))
        (when (slot-boundp object slot-name)
          (setf (slot-value copy slot-name)
            (slot-value object slot-name))))
      (apply #'reinitialize-instance copy initargs))))

答案 2 :(得分:0)

此解决方案不需要sl-mob

(defun copy-slot (s d slot)
  `(setf (,slot ,d) (,slot ,s)))

(defun copy-by-slots (s d slots)
  (assert (eql (class-of s) (class-of d)))
  (let ((f (lambda (s$) (eval (copy-slot s d s$)))))
    (mapcar f slots)))

(copy-by-slots src dest quoted-list-of-slots)

答案 3 :(得分:-1)

我提到了一个肮脏的把戏,它产生了CLOS实例的克隆。

(defclass cl () ((sl1 :initarg :sl1) (sl2 :initarg :sl2)))

(defmethod update-instance-for-different-class ((copy cl) (original cl) &key)
  (setf clone copy))

(setf a (make-instance 'cl :sl1 111 :sl2 222))

(change-class a 'cl)

(eq clone a) -> NIL
(eql (slot-value a 'sl1) (slot-value clone 'sl1)) -> T

隐喻CLOS本身需要克隆的概念。