2012-06-16 11 views
6

Ich bin auf der Suche nach einer Möglichkeit, CLOS-Objekte in einer flachen Art zu klonen, so dass das erstellte Objekt vom gleichen Typ mit den gleichen Werten in jedem Steckplatz, aber eine neue Instanz sein würde. Die nächste Sache, die ich gefunden habe, ist eine Standardfunktions-Kopierstruktur, die dies für Strukturen tut.Gibt es eine generische Methode zum Klonen von CLOS-Objekten?

Antwort

10

Es gibt keinen standardmäßigen vordefinierten Weg zum Kopieren von CLOS-Objekten im Allgemeinen. Es ist nicht trivial, wenn überhaupt, eine vernünftige Standardkopieoperation bereitzustellen, die (zumindest) die meiste Zeit für beliebige Objekte das Richtige tut, da sich die korrekte Semantik von Klasse zu Klasse und von Anwendung zu Anwendung ändert. Die erweiterten Möglichkeiten, die das MOP bietet, machen es noch schwieriger, einen solchen Standard bereitzustellen. Auch in CL, das eine in Müllsammlung gesammelte Sprache ist, wird das Kopieren von Objekten nicht sehr oft benötigt, z. wenn sie als Parameter übergeben oder zurückgegeben werden. Daher wäre die Implementierung Ihrer Kopiervorgänge nach Bedarf wahrscheinlich die sauberste Lösung.

aber sagen, dass hier ist das, was ich in einem meiner Schnipsel-Dateien gefunden, die möglicherweise tun, was Sie wollen:

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

Sie werden einige MOP Unterstützung für class-slots und slot-definition-name benötigen.

(I angenommen wahrscheinlich das von an old c.l.l thread, aber ich kann mich nicht erinnern, ich nie wirklich so etwas wie dies erforderlich ist, es ist so ganz und gar nicht getestet..)

Sie es wie folgt aus (getestet mit CCL) verwenden können:

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

Es kann nützlich sein, einen Test hinzuzufügen, wenn ein Slot gebunden ist oder nicht. Greifen Sie dann nur auf den Slot-Wert zu, wenn der Slot gebunden ist. –

+1

Sie haben Recht - ich habe den Test hinzugefügt. Vielen Dank! – danlei

+1

Funktioniert wie beschrieben. Hier ist eine Import-Anweisung, sollte es in einer mehr oder weniger tragbaren Art und Weise funktioniert: '(: Shadowing-Import-von \t # + OpenMCL-native-Fäden #: CCL \t # + CMU #: pcl \t # + sbcl #: sb-pcl \t # + # LispWorks: hcl \t # + allegro #: Mopp \t # + # clisp: clos \t #: Klasse-Slots #: Slot-Definition-name) '. – Inaimathi

4

Hier ist eine etwas andere Version der Funktion von Danlei vorgelegt. Ich habe das vor einer Weile geschrieben und bin gerade über diesen Beitrag gestolpert. Aus Gründen, die ich nicht vollständig erinnere, ruft dies nach dem Kopieren REINITIALIZE-INSTANCE auf. I denken Sie es ist so, dass Sie einige Änderungen an dem neuen Objekt vornehmen konnten, indem Sie zusätzliche Initrarg für diese Funktion übergeben

z.

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

Dies ist auch definiert als generische Funktion über Objekte, die 'Standard-Objekt' sind. Was vielleicht oder nicht das Richtige ist.

(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

Genau das, was ich gesucht habe; Ich war überrascht, dass dies in Common Lisp nicht standardmäßig vorhanden ist. – MicroVirus

Verwandte Themen