Het is vaak nodig om een kopie van een waarde in Ruby te maken. Hoewel dit misschien eenvoudig lijkt, en het is voor eenvoudige objecten, zul je snel merken dat er vele valkuilen zijn zodra je een kopie van een gegevensstructuur met meerdere array of hashes op hetzelfde object moet maken..
Laten we eens kijken naar enkele eenvoudige code om te begrijpen wat er aan de hand is. Allereerst de toewijzingsoperator die een POD-type (Plain Old Data) gebruikt in Ruby.
a = 1
b = a
a + = 1
zet b
Hier maakt de toewijzingsoperator een kopie van de waarde van een en toewijzen aan b met behulp van de toewijzingsoperator. Eventuele wijzigingen in een zal niet worden weerspiegeld in b. Maar hoe zit het met iets complexers? Overweeg dit.
a = [1,2]
b = a
een << 3
zet b.inspect
Probeer voordat u het bovenstaande programma uitvoert, te raden wat de uitvoer zal zijn en waarom. Dit is niet hetzelfde als het vorige voorbeeld, wijzigingen aangebracht in een worden weerspiegeld in b, maar waarom? Dit komt omdat het object Array geen POD-type is. De toewijzingsoperator maakt geen kopie van de waarde, maar kopieert gewoon de referentie naar het Array-object. De een en b variabelen zijn nu referenties naar hetzelfde Array-object, worden wijzigingen in beide variabelen in de andere gezien.
En nu kunt u zien waarom het kopiëren van niet-triviale objecten met verwijzingen naar andere objecten lastig kan zijn. Als u gewoon een kopie van het object maakt, kopieert u alleen de verwijzingen naar de diepere objecten, dus wordt uw kopie een "ondiepe kopie" genoemd.
Ruby biedt twee methoden voor het maken van kopieën van objecten, waaronder een die kan worden gemaakt om diepe kopieën te maken. De Object # dup methode maakt een ondiepe kopie van een object. Om dit te bereiken, de dup methode roept de initialize_copy methode van die klasse. Wat dit precies doet, is afhankelijk van de klas. In sommige klassen, zoals Array, wordt een nieuwe array geïnitialiseerd met dezelfde leden als de oorspronkelijke array. Dit is echter geen diepe kopie. Stel je de volgende situatie voor.
a = [1,2]
b = a.dup
een << 3
zet b.inspect
a = [[1,2]]
b = a.dup
a [0] << 3
zet b.inspect
Wat is hier gebeurd? De Array # initialize_copy methode maakt inderdaad een kopie van een array, maar die kopie is zelf een ondiepe kopie. Als u andere niet-POD-typen in uw array hebt, gebruikt u dup zal slechts een gedeeltelijk diepe kopie zijn. Het zal slechts zo diep zijn als de eerste array, diepere arrays, hashes of andere objecten worden alleen oppervlakkig gekopieerd.
Er is nog een methode die het vermelden waard is, kloon. De kloonmethode doet hetzelfde als dup met een belangrijk onderscheid: verwacht wordt dat objecten deze methode zullen vervangen door een methode die diepe kopieën kan maken.
Dus wat betekent dit in de praktijk? Het betekent dat elk van uw klassen een kloonmethode kan definiëren die een diepe kopie van dat object maakt. Het betekent ook dat je een kloonmethode moet schrijven voor elke klas die je maakt.
Een object 'rangschikken' is een andere manier om een object te 'serialiseren'. Met andere woorden, verander dat object in een karakterstream die naar een bestand kan worden geschreven dat je later kunt "unmarshaleren" of "unserialiseren" om hetzelfde object te krijgen. Dit kan worden benut om een diepe kopie van elk object te krijgen.
a = [[1,2]]
b = Marshal.load (Marshal.dump (a))
a [0] << 3
zet b.inspect
Wat is hier gebeurd?? Marshal.dump maakt een "dump" van de geneste array opgeslagen in een. Deze dump is een binaire tekenreeks die bedoeld is om in een bestand te worden opgeslagen. Het bevat de volledige inhoud van de array, een complete diepe kopie. De volgende, Marshal.load doet het tegenovergestelde. Het ontleedt deze binaire reeks tekens en maakt een volledig nieuwe array, met volledig nieuwe arrayelementen.