2013-05-09 27 views
8

Ich suche eine tiefe Kopie eines gesegneten Objekts (an dieser Stelle seicht vielleicht).Perl: Wie man ein gesegnetes Objekt tief kopiert?

Foo Klasse

package Foo; 
our $FOO = new Foo;  # initial run 

sub new { 
    my $class = shift; 
    my $self = {}; 
    bless $self, $class; 
    return $self; 
} 

Hauptprogramm

use Foo; 
my $copy = $Foo::FOO;  # instead of creating a ref, want to deep copy here 
$copy->{bar} = 'bar'; 

bar sowohl erscheint in $Foo::FOO und $copy. Ich weiß, dass ich eine Kopie des Objekts erstellen konnte, indem ich es als einrichtete, aber dann wäre es nicht mehr gesegnet; Außerdem würde dies nur für einfache Datenstrukturen funktionieren (momentan kein Problem). Ist die einzige Möglichkeit, diesen Weg zu kopieren und danach zu segnen (zB $copy = bless { %{$Foo::FOO} }, q{Foo};)?

Ich versuche, die Verwendung von Moose, Clone oder anderen Nicht-Core-Modulen/-Paketen zu vermeiden. Bitte beachten Sie dies bei der Antwort. So fett, dass es mehr herausragt :)

+0

Akzeptierte Lösung kann sich ändern: Beachten Sie, dass es mehr für das flache Kopieren einfacher Datenstrukturen ist, aber tiefes Kopieren oder sogar höher entwickelte Klassenstrukturen nicht lösen würde; Das war meine ursprüngliche Frage. In diesem Sinne kann sich die ausgewählte Antwort in Zukunft ändern. – vol7ron

Antwort

10

Das Kopieren sollte Teil der API sein. Der Benutzer Ihres Moduls würde niemals wissen, welche speziellen Aktionen bei der Erstellung eines neuen Objekts erforderlich sind (in Erwägung ziehen, jedes Objekt in einem my Hash in Ihrem Paket zu registrieren).

Daher bieten Sie eine clone Methode für Ihre Objekte. Darin können Sie alle schmutzigen Tricks verwenden, die Sie mögen:

sub clone { 
    my $self = shift; 
    my $copy = bless { %$self }, ref $self; 
    $register{$copy} = localtime; # Or whatever else you need to do with a new object. 
    # ... 
    return $copy; 
} 
+0

Ich denke, das könnte der richtige Weg sein. Dies wäre mein nächster Schritt gewesen, ich hatte gehofft, dass Perl etwas eingebaut hatte, das dasselbe machte, so dass ich mein Objekt nicht aufblasen musste, aber danke! – vol7ron

+0

Zusätzlich liest 'my $ dictionary = clone $ book;' viel besser. Obwohl dies die akzeptierte Antwort ist, sollten Benutzer beachten, dass dies mehr für oberflächliche Kopien von einfachen Datenstrukturen ist und tiefes Kopieren oder sogar komplexere Klassenstrukturen nicht lösen würde. – vol7ron

+2

Die Methode 'bless {% $ self}, ref $ self' wird nur eine oberflächliche Kopie erstellen. Attribute von '$ self', die Referenzen sind, werden nicht geklont. Zum Beispiel: $ obj -> {Ponys} = [qw (Dash Sparkle Jack)]; $ clone = $ obj-> Klon; push @ {$ clone -> {ponies}}, "Pinkie" 'ändert beide Objekte. – Schwern

9

use Storable 'dclone';?

$ corelist Storable 

Storable was first released with perl v5.7.3 

Plus können Sie mit Storable Haken herumzudaddeln feinere Steuerung zu behaupten, über Ihre Objekte zu kopieren (nicht, dass ich es getan habe, aber das ist, was die Dokumentation Ansprüche).

3

Es gibt keine gute Möglichkeit für das aufrufende Programm zu wissen, was "Kopieren eines Objekts" bedeutet, also sollte ein Objekt wissen, wie es sich selbst kopiert. Perl OO Sie bietet keine Hilfe beim hier, aber das herkömmliche, was zu tun ist so etwas wie dieses:

package Car; 

sub clone { 
    my ($self) = @_; 

    return $self->new(
     (map { $_ => $self->$_() } qw/make model/), # built-in types 
     engine => $self->engine->clone(), # copying an object 
    ); 
} 
4

my $copy = bless { %$self }, ref $self; in @ chorba Antwort ist unzureichend. Es wird nur die erste Ebene klonen. Alle in $self gespeicherten Referenzen werden nicht geklont. Die Folgen davon sind ...

$obj->{ponies} = [qw(Dash Sparkle Jack)]; 
$clone = $obj->clone; 
push @{$clone->{ponies}}, "Pinkie"; 
print join ", ", @{$obj->{ponies}}; # Dash Sparkle Jack Pinkie 

Sie haben vielleicht keine Referenzen jetzt, aber Sie werden wahrscheinlich später. Oder jemand anderes wird einen in dein Objekt stecken. Oder sie unterklassifizieren und fügen eins hinzu.

Sie können eine tiefe Klonroutine schreiben, aber es ist nicht einfach. Ich würde sehr empfehlen, Clone zu verwenden. Es hat keine Abhängigkeiten und Sie können Clone.pm einfach in Ihr Projekt kopieren.

Die andere Alternative ist Storable::dclone, erwähnt von @Zaid, die seit langer Zeit im Kern ist.

Unabhängig davon, was Sie verwenden, ist die Bereitstellung einer Klonmethode für Ihre Klasse das Richtige, auch wenn es nur ein Wrapper um Clone oder Storable :: dclone ist. Dies schützt den Benutzer Ihres Objekts vor den Details, wie Ihr Objekt geklont wird.

+0

Ich würde sagen, dass Sie manchmal wollen, dass die beiden geklonten Objekte auf den gleichen Referenten zeigen. Zum Beispiel, wenn Ihre 'Person' Klasse ein' Arbeitgeber'-Attribut hatte, das auf ein Objekt verweist, das die 'Organisation' von AcmeCorp repräsentiert, würden Sie wahrscheinlich nicht wollen, dass Bob geklont wird, um auch AcmeCorp zu klonen. Diese Art von Entscheidungen können nur auf einer Basis für jede Klasse getroffen werden und sollten gründlich dokumentiert werden. – tobyink

+0

@tobyink Ja, genau, warum das Klonen über '$ obj-> clone' und nicht über' clone ($ obj) 'erfolgt. Das Objekt kann am besten entscheiden, wie es geklont werden soll. Was eine 'Klon'-Methode tun sollte, ist schlecht definiert. Ist es ein oberflächlicher Klon? Ein tiefer Klon? Ein "wie ich finde" Klon? Das Verhalten/Interface Ihrer Clone-Methode sollte durchdacht und definiert werden. Sie benötigen möglicherweise mehrere Klonmethoden. – Schwern

0

Es tut mir leid, ich kann diesen Satz nicht bemerken:

* Ich versuche, mit Elch, Klonen oder andere Nicht-Core-Module/Pakete zu vermeiden, so wenden Sie sich bitte, dass im Auge behalten, wenn die Beantwortung . Bolted, so dass es mehr herausragt :) *

so konnte diese Antwort nicht akzeptiert werden!

+0

Erklären Sie, wie es das Problem löst. –

+0

Hallo, Rahil Wazir, es gibt Website-URL: http: //search.cpan.org/~ams/Storable-2.45/Storable.pm, können Sie herausfinden: "Storable bietet Ihnen eine Dclone-Schnittstelle, die das nicht erstellt intermediärer Skalar, sondern friert stattdessen die Struktur in einem internen Speicher ein und taut sie dann sofort auf. "," Das Herz von Storable ist in C geschrieben für eine ordentliche Geschwindigkeit. Bei der Manipulation von Perl-Interna wurden zusätzliche Low-Level-Optimierungen vorgenommen, um die Kapselung zu opfern zugunsten einer größeren Geschwindigkeit. "; Ich hoffe, dass dir die Dinge helfen können. –

+1

Können Sie diesen Teil in Ihre Antwort bearbeiten, anstatt ihn zu kommentieren. –