Hoe maak je diepe kopieën in Ruby

click fraud protection

Vaak is het nodig om een ​​kopie te maken van een waarde in Ruby. Hoewel dit misschien eenvoudig lijkt, en het is voor eenvoudige objecten, zodra u een kopie van een gegevens moet maken structuur met meerdere array of hashes op hetzelfde object, zult u snel merken dat er veel zijn valkuilen.

Objecten en referenties

Laten we eens kijken naar een simpele code om te begrijpen wat er aan de hand is. Ten eerste, de toewijzingsoperator die een POD (Plain Old Data) -type gebruikt Robijn.

a = 1
b = een
een + = 1
zet b

Hier maakt de toewijzingsoperator een kopie van de waarde van een en het 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 = een
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 een worden weerspiegeld in b, maar waarom? Dit komt omdat de

instagram viewer
Array object is geen POD-type. De toewijzingsoperator maakt geen kopie van de waarde, hij kopieert gewoon de referentie naar het Array-object. De een en b variabelen zijn nu referenties voor hetzelfde Array-object, worden alle wijzigingen in een van de variabelen in de andere gezien.

En nu kunt u zien waarom het lastig kan zijn om niet-triviale objecten te kopiëren met verwijzingen naar andere objecten. Als u gewoon een kopie van het object maakt, kopieert u alleen de verwijzingen naar de diepere objecten, dus uw kopie wordt een "ondiepe kopie" genoemd.

Wat Ruby biedt: dup en kloon

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 zal 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
een [0] << 3
zet b.inspect

Wat is er hier gebeurd? De Matrix # 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 heeft, gebruikt u dup zal slechts een gedeeltelijk diepe kopie zijn. Het zal slechts zo diep zijn als de eerste array, dieper arrays, hashes of andere objecten worden slechts oppervlakkig gekopieerd.

Er is nog een andere methode die het vermelden waard is, kloon. De kloonmethode doet hetzelfde als dup met één belangrijk onderscheid: naar verwachting zullen objecten deze methode vervangen door een methode die diepe kopieën kan maken.

Dus wat betekent dit in de praktijk? Het betekent dat al uw klassen een kloonmethode kunnen definiëren die een diepe kopie van dat object zal maken. Het betekent ook dat je voor elke klasse die je maakt een kloonmethode moet schrijven.

A Trick: Marshalling

Een object 'marshallen' is een andere manier om een ​​object te 'serialiseren'. Met andere woorden, verander dat object in een karakterstroom die naar een bestand kan worden geschreven dat je later kunt "unmarshal" of "unerialize" 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))
een [0] << 3
zet b.inspect

Wat is er hier gebeurd? Marshal.dump maakt een "dump" van de geneste array die is 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 volledige diepe kopie. De volgende, Marshal. Laden doet het tegenovergestelde. Het parseert deze binaire karakter array en creëert een compleet nieuwe array, met compleet nieuwe array-elementen.

Maar dit is een truc. Het is inefficiënt, het werkt niet op alle objecten (wat gebeurt er als je op deze manier een netwerkverbinding probeert te klonen?) En het is waarschijnlijk niet erg snel. Het is echter de gemakkelijkste manier om diepe kopieën te maken die niet op maat zijn initialize_copy of kloon methoden. Hetzelfde kan ook worden gedaan met methoden zoals to_yaml of to_xml als je bibliotheken hebt geladen om ze te ondersteunen.

instagram story viewer