Usein on tarpeen tehdä kopio arvo rubyina. Vaikka tämä saattaa tuntua yksinkertaiselta, ja se on tarkoitettu yksinkertaisille kohteille, heti kun sinun on tehtävä kopio tiedoista rakenne, jossa on useita matriiseja tai hashia samassa objektissa, löydät nopeasti, että niitä on paljon sudenkuoppia.
Kohteet ja viitteet
Tarkastellaan yksinkertaista koodia ymmärtääksesi mitä tapahtuu. Ensinnäkin määritysoperaattori, joka käyttää POD (Plain Old Data) -tyyppiä Rubiini.
a = 1
b = a
a + = 1
asettaa b
Täällä toimeksiantaja tekee kopion arvosta ja osoittamalla se b käyttämällä toimeksiantajaa. Kaikki muutokset ei heijastu b. Entä jos jotain monimutkaisempaa? Harkitse tätä.
a = [1,2]
b = a
a << 3
asettaa tarkastajan
Ennen yllä olevan ohjelman suorittamista yritä arvata mikä tulos ja miksi. Tämä ei ole sama kuin edellinen esimerkki heijastuvat b, mutta miksi? Tämä johtuu siitä, että ryhmä objekti ei ole POD-tyyppi. Tehtäväoperaattori ei kopioi arvoa, se vain kopioi arvon viite Array-objektiin. ja b muuttujat ovat nyt
viittaukset samaan Array-objektiin, muutokset kummassakin muuttujassa näkyvät toisessa.Ja nyt näet, miksi ei-triviaalisten objektien kopioiminen viittauksella muihin kohteisiin voi olla hankalaa. Jos teet vain kopion objektista, kopioit vain viittauksia syvempiin objekteihin, joten kopioon viitataan nimellä "matala kopio".
Mitä Ruby tarjoaa: dup ja klooni
Ruby tarjoaa kaksi tapaa kopioida esineitä, mukaan lukien yhden, joka voidaan tehdä syväkopioksi. Esine # dup menetelmä tekee matalasta kopiosta objektista. Tämän saavuttamiseksi dup menetelmä kutsuu initialize_copy luokan menetelmä. Mitä tämä tarkalleen tekee, riippuu luokasta. Joissakin luokissa, kuten taulukko, se alustaa uuden taulukon samoilla jäsenillä kuin alkuperäinen taulukko. Tämä ei kuitenkaan ole syvä kopio. Harkitse seuraavaa.
a = [1,2]
b = a.dup
a << 3
asettaa tarkastajan
a = [[1,2]]
b = a.dup
a [0] << 3
asettaa tarkastajan
Mitä täällä on tapahtunut? Array # initialize_copy menetelmä tekee todellakin kopion taulukosta, mutta se on sinänsä matala kopio. Jos taulukossa on muita kuin POD-tyyppejä, käytä dup tulee olemaan vain osittain syvä kopio. Se on vain niin syvä kuin ensimmäinen ryhmä, syvemmälle taulukot, hashes tai muut esineet kopioidaan vain matalaksi.
On toinen mainitsemisen arvoinen menetelmä, klooni. Kloonimenetelmä tekee saman kuin dup yhdellä tärkeällä erotuksella: on odotettavissa, että esineet ohittavat tämän menetelmän sellaisella, jolla voidaan tehdä syväkopioita.
Joten mitä tämä tarkoittaa käytännössä? Se tarkoittaa, että jokainen luokkasi voi määrittää kloonimenetelmän, joka tekee syvän kopion kyseisestä objektista. Se tarkoittaa myös, että sinun on kirjoitettava kloonimenetelmä jokaiselle tekemällesi luokalle.
Temppu: Marshalling
Objektin "marshalling" on toinen tapa sanoa objektin "serialization". Toisin sanoen, muuta tämä objekti merkkivirraksi, joka voidaan kirjoittaa tiedostoon, jonka voit myöhemmin "unmarshal" tai "unserialize" saadaksesi saman objektin. Tätä voidaan hyödyntää saadaksesi syvän kopion mistä tahansa objektista.
a = [[1,2]]
b = marshal.load (marshal.dump (a))
a [0] << 3
asettaa tarkastajan
Mitä täällä on tapahtunut? Marshal.dump luo "jätteen" sisäkkäisestä taulukosta, joka on tallennettu . Tämä dump on binaarimerkkijono, joka on tarkoitettu tallennettavaksi tiedostoon. Siinä on taulukon koko sisältö, täydellinen syväkopio. Seuraava, Marshal.load tekee päinvastoin. Se jäsentää tämän binaarimerkkijonojen ja luo täysin uuden taulukon, jossa on täysin uudet taulukkoelementit.
Mutta tämä on temppu. Se on tehoton, se ei toimi kaikissa kohteissa (mitä tapahtuu, jos yrität kloonata verkkoyhteyttä tällä tavalla?), Eikä se todennäköisesti ole kovin nopeaa. Se on kuitenkin helpoin tapa tehdä syväkopioita ilman räätälöityjä initialize_copy tai klooni menetelmiä. Sama asia voidaan tehdä myös sellaisilla menetelmillä to_yaml tai to_xml jos sinulla on kirjastoja ladattu tukemaan niitä.