Hogyan készítsen mély másolatot Ruby-ban

Gyakran szükséges másolatot készíteni a érték Rubyban. Noha ez egyszerűnek tűnhet, és egyszerű objektumok esetében, amint el kell készítenie az adatok másolatát szerkezet több tömb vagy kivonat ugyanazon az objektumon, gyorsan megtalálja sok buktatókat.

Tárgyak és referenciák

Nézzük meg néhány egyszerű kódot, hogy megértsük, mi folyik itt. Először a hozzárendelési operátor POD-t (Plain Old Data) használ Rubin.

a = 1
b = a
a + = 1
felveszi b

Itt a hozzárendelési operátor másolatot készít a (z) értékről egy és hozzárendelni b a hozzárendelési operátor segítségével. A egy nem tükröződik benne b. De mi lenne valami összetettebbnel? Ezt fontold meg.

a = [1,2]
b = a
a << 3
felhívja a figyelmet

A fenti program futtatása előtt próbálja meg kitalálni, hogy mi lesz a kimenet és miért. Ez nem ugyanaz, mint az előző példa egy tükröződnek a b, de miért? Ennek oka az, hogy a Sor az objektum nem POD típus. A hozzárendelési operátor nem készít másolatot az értékről, hanem egyszerűen lemásolja az értéket referencia

instagram viewer
a Array objektumhoz. Az egy és b a változók most referenciák ugyanahhoz a tömb objektumhoz, bármelyik változóban bekövetkező változások a másikban láthatók.

És most láthatja, hogy miért nehéz lehet a nem triviális objektumok másolása más objektumokra való hivatkozással. Ha egyszerűen csak másolatot készít az objektumról, akkor csak a mélyebb objektumokra való hivatkozásokat másolja, így a másolatot "sekély másolatnak" nevezik.

Mit nyújt a Ruby: dupla és klón?

A Ruby kétféle módszert kínál tárgyak másolatainak készítésére, köztük egy mélyreható másolatok készítésére is. Az Tárgy # dup metódus sekély másolatot készít egy objektumból. Ennek elérése érdekében a dup módszer hívja a initialize_copy az osztály módszere. Hogy ez pontosan mit csinál, az osztálytól függ. Egyes osztályokban, például a tömb, az új tömböt inicializálja ugyanazokkal a tagokkal, mint az eredeti tömb. Ez azonban nem mély másolat. Tekintsük a következő.

a = [1,2]
b = a.dup
a << 3
felhívja a figyelmet
a = [[1,2]]
b = a.dup
a [0] << 3
felhívja a figyelmet

Mi történt itt? Az Array # initialize_copy A módszer valóban elkészíti a tömb másolatát, de maga a másolat sekély. Ha van más nem POD típus a tömbben, akkor használja a dup csak részleges mély példány lesz. Csak annyira mély lesz, mint az első tömb, bármilyen mélyebb is tömbök, hash vagy más tárgyakat csak sekély módon másolnak.

Van még egy említésre méltó módszer, klón. A klón módszer ugyanazt teszi, mint a dup egy fontos megkülönböztetéssel: várható, hogy az objektumok felülírják ezt a módszert egy olyan módszerrel, amely mély másolatot készíthet.

Tehát a gyakorlatban mit jelent ez? Ez azt jelenti, hogy minden osztály meghatározhat egy olyan klónozási módszert, amely mély másolatot készít az objektumról. Ez azt is jelenti, hogy minden egyes osztályhoz klónozási módszert kell írni.

Trükk: rendezés

Az objektum „rendezése” egy másik módszer az objektum „sorosítása”. Más szavakkal: ezt az objektumot karakterfolyammá alakíthatja, amelyet olyan fájlba lehet írni, amelyet később "unmarshal" vagy "unserialize" lehet, hogy ugyanazt az objektumot kapja. Ezt ki lehet használni bármilyen objektum mély másolatának elkészítéséhez.

a = [[1,2]]
b = Marshal.load (Marshal.dump (a))
a [0] << 3
felhívja a figyelmet

Mi történt itt? Marshal.dump létrehoz egy "dump" -t a beágyazott tömbből, amely a egy. Ez a dump egy bináris karakterlánc, amelyet fájlban kell tárolni. Itt található a tömb teljes tartalma, egy teljes mély példány. Következő, Marshal.load az ellenkezőjét csinálja. Elemezte ezt a bináris karakter tömböt, és létrehoz egy teljesen új tömböt, teljesen új tömb elemekkel.

De ez egy trükk. Nem hatékony, nem működik minden objektumon (mi történik, ha ilyen módon próbálsz klónozni egy hálózati kapcsolatot?), És valószínűleg nem is rendkívül gyors. Ennek ellenére a legegyszerűbb módja annak, hogy mélyreható másolatokat készítsen a szokásoshoz képest initialize_copy vagy klón mód. Ugyanígy lehet ugyanezt megtenni olyan módszerekkel is, mint a to_yaml vagy to_xml ha van könyvtárak betöltve őket támogatni.