Es ist oft notwendig, eine Kopie eines Werts in Ruby zu erstellen. Dies mag einfach erscheinen, und dies gilt auch für einfache Objekte. Sobald Sie eine Kopie einer Datenstruktur mit mehreren Arrays oder Hashes für dasselbe Objekt erstellen müssen, werden Sie schnell feststellen, dass es viele Fallstricke gibt.
Um zu verstehen, was los ist, schauen wir uns einen einfachen Code an. Zuerst den Zuweisungsoperator, der einen POD-Typ (Plain Old Data) in Ruby verwendet.
a = 1
b = a
a + = 1
setzt b
Hier erstellt der Zuweisungsoperator eine Kopie des Werts von ein und zuweisen b mit dem Zuweisungsoperator. Änderungen an ein wird nicht reflektiert b. Aber was ist mit etwas Komplexerem? Bedenken Sie.
a = [1,2]
b = a
ein << 3
setzt b.inspect
Versuchen Sie vor dem Ausführen des obigen Programms zu erraten, wie und warum die Ausgabe aussehen wird. Dies ist nicht dasselbe wie im vorherigen Beispiel, an dem Änderungen vorgenommen wurden ein spiegeln sich in b, aber wieso? Dies liegt daran, dass das Array-Objekt kein POD-Typ ist. Der Zuweisungsoperator erstellt keine Kopie des Werts, sondern kopiert einfach den Referenz auf das Array-Objekt. Das ein und b Variablen sind jetzt Verweise Für dasselbe Array-Objekt werden alle Änderungen an einer der Variablen in der anderen angezeigt.
Und jetzt sehen Sie, warum das Kopieren von nicht trivialen Objekten mit Verweisen auf andere Objekte schwierig sein kann. Wenn Sie lediglich eine Kopie des Objekts erstellen, kopieren Sie lediglich die Verweise auf die tieferen Objekte, sodass Ihre Kopie als "flache Kopie" bezeichnet wird.
Ruby stellt zwei Methoden zum Erstellen von Kopien von Objekten zur Verfügung, 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 wird die aufrufen 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
ein << 3
setzt b.inspect
a = [[1,2]]
b = a.dup
a [0] << 3
setzt b.inspect
Was ist hier passiert? Das Array # initialize_copy Methode wird in der Tat eine Kopie eines Arrays erstellen, aber diese Kopie ist 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 ist nur so tief wie das erste Array, alle tieferen Arrays, Hashes oder anderen 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 überschreiben, die tiefe kopien erstellen kann.
Was bedeutet das in der Praxis? Dies bedeutet, dass jede Ihrer Klassen eine Klonmethode definieren kann, mit der eine gründliche Kopie dieses Objekts erstellt wird. Es bedeutet auch, dass Sie für jede Klasse, die Sie erstellen, eine Klonmethode schreiben müssen.
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 "unmarshalen" oder "unserialisieren" können, um dasselbe Objekt zu erhalten. Dies kann ausgenutzt werden, um eine tiefe Kopie eines 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 verschachtelten Arrays, das in gespeichert ist ein. Dieser Speicherauszug ist eine binäre Zeichenfolge, die in einer Datei gespeichert werden soll. Es enthält den gesamten Inhalt des Arrays, eine vollständige, tiefe Kopie. Nächster, Marschall.Laden macht das Gegenteil. Es analysiert dieses binäre Zeichenarray und erstellt ein komplett neues Array mit komplett neuen Array-Elementen.