Ako urobiť hlboké kópie v Ruby

Často je potrebné vytvoriť kópiu hodnota v Ruby. Aj keď sa to môže zdať jednoduché a je to pre jednoduché objekty, akonáhle budete musieť vytvoriť kópiu údajov štruktúra s viacerými poliami alebo hashami na rovnakom objekte, rýchlo zistíte, že ich je veľa úskalia.

Predmety a odkazy

Aby sme pochopili, čo sa deje, pozrime sa na jednoduchý kód. Najprv operátor priradenia používajúci typ POD (Plain Old Data) v systéme rubín.

a = 1
b = a
a + = 1
kladie b

Operátor priradenia tu vytvára kópiu hodnoty a priradiť ho b pomocou operátora priradenia. Akékoľvek zmeny v nebude sa odrážať v b. Ale čo niečo zložitejšie? Zváž toto.

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

Pred spustením vyššie uvedeného programu sa pokúste uhádnuť, aký bude výstup a prečo. To nie je to isté ako v predchádzajúcom príklade sa odrážajú v b, ale prečo? Je to preto, že rad objekt nie je typu POD. Operátor priradenia nevytvára kópiu hodnoty, iba kopíruje referencie k objektu Array. a b premenné sú teraz referencie k rovnakému objektu Array sa všetky zmeny v oboch premenných prejavia v druhej.

instagram viewer

A teraz vidíte, prečo je kopírovanie netriviálnych objektov s odkazmi na iné objekty zložité. Ak jednoducho vytvoríte kópiu objektu, kopírujete iba odkazy na hlbšie objekty, takže vaša kópia sa označuje ako „plytká kópia“.

Čo Ruby poskytuje: duplikujte a klonujte

Ruby poskytuje dva spôsoby vytvárania kópií objektov, vrátane jedného, ​​ktorý sa dá vytvoriť na vytváranie hlbokých kópií. Predmet # dup metóda vytvorí plytkú kópiu objektu. Na dosiahnutie tohto cieľa dup metóda bude volať initialize_copy metóda tejto triedy. To, čo presne robí, závisí od triedy. V niektorých triedach, ako napríklad Array, inicializuje nové pole s rovnakými členmi ako pôvodné pole. Toto však nie je hĺbková kópia. Zvážte nasledujúce.

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

Čo sa tu stalo? Array # initialize_copy metóda skutočne vytvorí kópiu poľa, ale táto kópia je sama o sebe plytkou kópiou. Ak máte vo svojom poli iné typy, ktoré nie sú POD, použite dup bude to iba čiastočne hlboká kópia. Bude to tak hlboké ako prvé pole, hlbšie polí, hashes alebo iné objekty sa skopírujú iba na plytké účely.

Je treba spomenúť aj inú metódu, clone. Klonovacia metóda robí to isté ako dup s jedným dôležitým rozlíšením: očakáva sa, že objekty prepíšu túto metódu s metódou, ktorá dokáže vytvárať hlboké kópie.

Čo to v praxi znamená? Znamená to, že každá z vašich tried môže definovať klonovaciu metódu, ktorá vytvorí hlbokú kópiu daného objektu. Znamená to tiež, že musíte napísať klonovaciu metódu pre každú triedu, ktorú vytvoríte.

Trik: Radenie

„Zoraďovanie“ objektu je ďalší spôsob, ako povedať „serializáciu“ objektu. Inými slovami, premente tento objekt na tok znakov, ktorý je možné zapísať do súboru, ktorý môžete neskôr „unmarshal“ alebo „unserialize“ získať a získať ten istý objekt. To možno využiť na získanie hlbokej kópie ľubovoľného objektu.

a = [[1,2]]
b = maršal.load (maršal.dump (a))
a [0] << 3
kladie b.inspect

Čo sa tu stalo? Marshal.dump vytvorí "výpis" vnoreného poľa uloženého v . Tento výpis je reťazec binárnych znakov určený na uloženie do súboru. Obsahuje celý obsah poľa, úplnú hĺbkovú kópiu. Ďalšie, Marshal.load robí opak. Analyzuje toto pole binárnych znakov a vytvára úplne nové pole s úplne novými prvkami poľa.

Ale to je trik. Je to neefektívne, nebude fungovať na všetkých objektoch (čo sa stane, ak sa pokúsite klonovať sieťové pripojenie týmto spôsobom?) A pravdepodobne to nebude strašne rýchle. Je to však najjednoduchší spôsob, ako zmenšiť kópie na mieru initialize_copy alebo clone metódy. To isté sa dá urobiť aj pomocou metód to_yaml alebo to_xml ak máte načítané knižnice, ktoré ich podporujú.