So erstellen Sie tiefe Kopien in Ruby

Es ist oft notwendig, eine Kopie von a zu machen Wert in Ruby. Dies mag einfach erscheinen und gilt für einfache Objekte, sobald Sie eine Kopie der Daten erstellen müssen Struktur mit mehreren Arrays oder Hashes auf dem gleichen Objekt, werden Sie schnell feststellen, dass es viele gibt Tücken.

Objekte und Referenzen

Um zu verstehen, was los ist, schauen wir uns einen einfachen Code an. Zuerst gibt der Zuweisungsoperator, der einen POD (Plain Old Data) verwendet, ein Rubin.

a = 1
b = a
a + = 1
setzt b

Hier erstellt der Zuweisungsoperator eine Kopie des Werts von ein und zuweisen b Verwenden des Zuweisungsoperators. Änderungen an ein wird nicht reflektiert b. Aber was ist mit etwas Komplexerem? Bedenken Sie.

a = [1,2]
b = a
a << 3
setzt b.inspect

Versuchen Sie vor dem Ausführen des obigen Programms zu erraten, wie die Ausgabe aussehen wird und warum. Dies ist nicht dasselbe wie im vorherigen Beispiel, an dem Änderungen vorgenommen wurden ein spiegeln sich in b, aber wieso? Das liegt daran, dass die

instagram viewer
Array Objekt ist kein POD-Typ. Der Zuweisungsoperator erstellt keine Kopie des Werts, sondern kopiert einfach den Referenz zum Array-Objekt. Das ein und b Variablen sind jetzt Verweise Für dasselbe Array-Objekt werden alle Änderungen in einer der Variablen in der anderen angezeigt.

Und jetzt können Sie sehen, warum das Kopieren nicht trivialer Objekte mit Verweisen auf andere Objekte schwierig sein kann. Wenn Sie einfach eine Kopie des Objekts erstellen, kopieren Sie nur die Verweise auf die tieferen Objekte, sodass Ihre Kopie als "flache Kopie" bezeichnet wird.

Was Ruby bietet: Dup und Klon

Ruby bietet zwei Methoden zum Erstellen von Kopien von Objekten, darunter eine, mit der tiefe Kopien erstellt werden können. Das Objekt # dup Methode erstellt eine flache Kopie eines Objekts. Um dies zu erreichen, muss die dup Methode ruft die initialize_copy Methode dieser Klasse. Was dies genau tut, hängt von der Klasse ab. In einigen Klassen, z. B. Array, wird ein neues Array mit denselben Elementen wie das ursprüngliche Array initialisiert. Dies ist jedoch keine tiefe Kopie. Folgendes berücksichtigen.

a = [1,2]
b = a.dup
a << 3
setzt b.inspect
a = [[1,2]]
b = a.dup
a [0] << 3
setzt b.inspect

Was ist hier passiert? Das Array # initialize_copy Die Methode erstellt zwar eine Kopie eines Arrays, diese Kopie ist jedoch selbst eine flache Kopie. Wenn Sie andere Nicht-POD-Typen in Ihrem Array haben, verwenden Sie dup wird nur eine teilweise tiefe Kopie sein. Es wird nur so tief sein wie das erste Array, noch tiefer Arrays, Hashes oder andere Objekte werden nur flach kopiert.

Es gibt noch eine andere erwähnenswerte Methode: Klon. Die Klonmethode macht dasselbe wie dup mit einem wichtigen Unterschied: Es wird erwartet, dass Objekte diese Methode mit einer Methode überschreiben, die tiefe Kopien erstellen kann.

Was bedeutet das in der Praxis? Dies bedeutet, dass jede Ihrer Klassen eine Klonmethode definieren kann, die eine tiefe Kopie dieses Objekts erstellt. Es bedeutet auch, dass Sie für jede Klasse, die Sie erstellen, eine Klonmethode schreiben müssen.

Ein Trick: Marshalling

Das "Marshalling" eines Objekts ist eine andere Art, ein Objekt "zu serialisieren". Mit anderen Worten, verwandeln Sie dieses Objekt in einen Zeichenstrom, der in eine Datei geschrieben werden kann, die Sie später "entmarschieren" oder "unserialisieren" können, um dasselbe Objekt zu erhalten. Dies kann ausgenutzt werden, um eine tiefe Kopie eines beliebigen Objekts zu erhalten.

a = [[1,2]]
b = Marshal.load (Marshal.dump (a))
a [0] << 3
setzt b.inspect

Was ist hier passiert? Marshal.dump Erstellt einen "Speicherauszug" des in gespeicherten verschachtelten Arrays ein. Dieser Speicherauszug ist eine binäre Zeichenfolge, die in einer Datei gespeichert werden soll. Es enthält den vollständigen Inhalt des Arrays, eine vollständige, tiefe Kopie. Nächster, Marschall.Ladung macht das Gegenteil. Es analysiert dieses binäre Zeichenarray und erstellt ein völlig neues Array mit völlig neuen Array-Elementen.

Aber das ist ein Trick. Es ist ineffizient, funktioniert nicht bei allen Objekten (was passiert, wenn Sie versuchen, eine Netzwerkverbindung auf diese Weise zu klonen?) Und ist wahrscheinlich nicht besonders schnell. Es ist jedoch der einfachste Weg, tiefe Kopien ohne benutzerdefinierte zu erstellen initialize_copy oder Klon Methoden. Das gleiche kann auch mit Methoden wie gemacht werden to_yaml oder to_xml wenn Sie Bibliotheken geladen haben, um sie zu unterstützen.