2012-06-16 8 views
6

Sto cercando un modo per clonare gli oggetti CLOS in modo superficiale, quindi l'oggetto creato sarebbe dello stesso tipo con gli stessi valori in ogni slot, ma una nuova istanza. La cosa più vicina che ho trovato è una funzione di copia della funzione standard che fa questo per le strutture.Esiste un metodo generico per clonare oggetti CLOS?

risposta

10

Non esiste un modo predefinito standard per copiare gli oggetti CLOS in generale. Non è banale, se possibile, fornire una ragionevole operazione di copia predefinita che faccia la cosa giusta (almeno) la maggior parte del tempo per oggetti arbitrari, poiché la semantica corretta cambia da una classe all'altra e dall'applicazione all'applicazione. Le estese possibilità offerte dal MOP rendono ancora più difficile fornire tale impostazione predefinita. Inoltre, in CL, essendo un linguaggio garbage collection, la copia di oggetti non è realmente necessaria molto spesso, ad es. quando passato come parametri o essere restituito. Quindi, implementare le operazioni di copia secondo necessità sarebbe probabilmente la soluzione più pulita.

Detto questo, qui è quello che ho trovato in uno dei miei file dei frammenti, che potrebbe fare quello che vuoi:

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

Avrete bisogno di qualche supporto MOP per class-slots e slot-definition-name.

(probabilmente adottato questo da an old c.l.l thread, ma non riesco a ricordare non ho mai veramente avuto bisogno di qualcosa di simile, quindi è assolutamente non testato..)

è possibile utilizzarlo in questo modo (testato con 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> 
+5

Potrebbe essere utile aggiungere un test se uno slot è vincolato o meno. Quindi accedere solo al valore dello slot, se lo slot è vincolato. –

+1

Hai ragione - Ho aggiunto il test. Grazie! – danlei

+1

Funziona come pubblicizzato. Ecco una dichiarazione di importazione che dovrebbe farlo funzionare in modo più o meno portabile: '(: shadowing-import-da \t # + OpenMCL-native-threads #: CCL \t # + CMU #: PCL \t # + SBCL #: sb-pcl \t # + lispworks #: HCl \t # + allegro #: scopa \t # + clisp #: clos \t #: cLASS-slots #: slot-definition-name) '. – Inaimathi

4

Ecco una versione leggermente diversa della funzione presentata da danlei. L'ho scritto qualche tempo fa e sono incappato in questo post. Per motivi che non ricordo completamente, questo chiama REINITIALIZE-INSTANCE dopo la copia. I penso è così è possibile apportare alcune modifiche al nuovo oggetto passando l'initargs aggiuntivo a questa funzione

ad es.

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

Questo è anche definito come funzione generica su oggetti che sono "oggetti standard". Quale potrebbe o potrebbe non essere la cosa giusta da fare.

(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)))) 
+1

Esattamente quello che stavo cercando; Sono stato sorpreso che questo non esiste di default in Common Lisp. – MicroVirus

Problemi correlati