Kako narediti globoke kopije v Rubyju

Pogosto je potrebno narediti kopijo vrednost v Ruby. Čeprav se to morda zdi preprosto in je za preproste predmete, takoj ko morate narediti kopijo podatkov strukturo z več nizi ali razpršitvami na isti objekt, hitro boste ugotovili, da jih je veliko pasti.

Predmeti in reference

Da bi razumeli, kaj se dogaja, poglejmo nekaj preprostih kod. Najprej operater dodeljevanja, ki uporablja tip POD (Navadni stari podatki) Ruby.

a = 1
b = a
a + = 1
postavlja b

Tu operater dodelitve naredi kopijo vrednosti a in temu dodeliti b z uporabo operaterja dodelitve. Morebitne spremembe a se ne bo odražalo v b. Kaj pa kaj bolj zapletenega? Upoštevajte to.

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

Preden zaženete zgornji program, poskusite uganiti, kakšen bo izhod in zakaj. To ni isto kot prejšnji primer sprememb a se odražajo v b, ampak zakaj? To je zato, ker je Niz objekt ni tipa POD. Operater dodeljevanja ne naredi kopije vrednosti, temveč preprosto kopira sklic do objekta Array. The a in b spremenljivke so zdaj reference istemu objektu Array, bodo vse spremembe katere koli spremenljivke vidne v drugi.

instagram viewer

In zdaj lahko vidite, zakaj je kopiranje netrivialnih predmetov s sklici na druge predmete lahko težavno. Če preprosto naredite kopijo predmeta, samo kopirate sklice na globlje predmete, zato se vaša kopija imenuje "plitva kopija".

Kaj ponuja Ruby: dup in klon

Ruby ponuja dva načina za izdelavo kopij predmetov, vključno z enim, ki ga lahko naredimo za globoko kopijo. The Objekt # dup metoda bo naredila plitvo kopijo predmeta. Da bi to dosegli, dvoj metoda pokliče Initialize_copy metoda tega razreda. Kaj točno to počne, je odvisno od razreda. V nekaterih razredih, kot je Array, bo inicializiral novo matriko z enakimi člani kot izvorni niz. To pa ni globoka kopija. Upoštevajte naslednje.

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

Kaj se je zgodilo tukaj? The Niz # Initialize_copy metoda bo sicer naredila kopijo matrike, vendar je ta kopija sama plitva. Če imate v nizu druge vrste, ki niso POD, uporabite dvoj bo le delno globoka kopija. Le globok bo kot prvi niz, kateri koli globlji nizi, hash ali bodo drugi predmeti le plitko kopirani.

Treba je omeniti še eno metodo, klon. Metoda kloniranja naredi isto kot dvoj z enim pomembnim razlikovanjem: pričakuje se, da bodo predmeti to metodo preglasili s tisto, ki lahko naredi globoke kopije.

Torej v praksi, kaj to pomeni? To pomeni, da lahko vsak vaš razred definira metodo kloniranja, ki bo naredila globoko kopijo tega predmeta. To pomeni tudi, da morate za vsak razred, ki ga naredite, napisati klonsko metodo.

Trik: Marshalling

"Marshalling" predmeta je še en način, kako reči "serijski" objekt. Z drugimi besedami, pretvorite ta objekt v tok znakov, ki ga je mogoče zapisati v datoteko, ki jo lahko kasneje "razveljavite" ali "neserializirate", da dobite isti objekt. To je mogoče izkoristiti za globinsko kopijo katerega koli predmeta.

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

Kaj se je zgodilo tukaj? Marshal.dump ustvari "smetišče" gnezdenega sklopa, shranjenega v a. Ta zapis je binarni niz znakov, namenjen shranjevanju v datoteki. Hrani celotno vsebino matrike, popolno globoko kopijo. Naslednji, Marshal.load počne nasprotno. Razčleni ta binarni niz znakov in ustvari popolnoma nov Array s popolnoma novimi elementi Array.

Toda to je trik. Neučinkovit je, ne bo deloval na vseh objektih (kaj se zgodi, če poskusite na tak način klonirati omrežno povezavo?) In verjetno ni grozno hiter. Vendar je najlažji način, da globoke kopije ne pridejo po meri Initialize_copy ali klon metod. Isto lahko storimo tudi z metodami, kot so to_yaml ali do_xml če imate naložene knjižnice, ki jih podpirajo.

instagram story viewer