Cum se fac copii adânci în rubin

Adesea este necesar să faceți o copie a valoare în Rubin. În timp ce acest lucru poate părea simplu și este pentru obiecte simple, imediat ce trebuie să faceți o copie a unei date structură cu mai multe tablouri sau hash-uri pe același obiect, veți găsi rapid că există multe capcane.

Obiecte și referințe

Pentru a înțelege ce se întâmplă, să ne uităm la niște coduri simple. În primul rând, operatorul de atribuire utilizând un tip POD (date vechi simple) în Rubin.

a = 1
b = a
a + = 1
pune b

Aici, operatorul de atribuire face o copie a valorii A și atribuirea lui b folosind operatorul de atribuire. Orice modificări la A nu va fi reflectat în b. Dar despre ceva mai complex? Gandeste-te la asta.

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

Înainte de a rula programul de mai sus, încercați să ghiciți care va fi rezultatul și de ce. Acesta nu este același lucru ca și exemplul anterior, cu modificările aduse A sunt reflectate în b, dar de ce? Acest lucru se datorează faptului că mulțime obiectul nu este de tip POD. Operatorul de atribuire nu face o copie a valorii, ci doar o copiază

instagram viewer
referinţă la obiectul Array. A și b variabilele sunt acum referințe la același obiect Array, orice alte modificări ale oricărei variabile vor fi văzute în cealaltă.

Și acum puteți vedea de ce copierea obiectelor non-banale cu referințe la alte obiecte poate fi dificilă. Dacă faceți pur și simplu o copie a obiectului, copiați doar referințele la obiectele mai adânci, astfel încât copia dvs. este denumită „copie superficială”.

Ce oferă Ruby: dup și clonă

Ruby oferă două metode pentru a face copii ale obiectelor, inclusiv una care poate fi făcută pentru a face copii profunde. Obiect # DUP metoda va face o copie superficială a unui obiect. Pentru a realiza acest lucru, DUP metoda va apela initialize_copy metoda clasei respective. Ceea ce face asta depinde exact de clasă. În unele clase, cum ar fi Array, va inițializa un nou tablou cu aceiași membri ca tabloul original. Totuși, aceasta nu este o copie profundă. Luați în considerare următoarele.

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

Ce s-a întâmplat aici? Array # initialize_copy metoda va face într-adevăr o copie a unui Array, dar această copie este ea însăși o copie superficială. Dacă aveți alte tipuri care nu sunt POD în tabloul dvs., utilizați DUP va fi doar o copie parțial profundă. Va fi la fel de adânc ca primul tablou, oricare mai profund matrice, hashes sau alte obiecte vor fi copiate puțin.

Există o altă metodă care merită menționată, clona. Metoda clonă face același lucru ca și DUP cu o distincție importantă: este de așteptat ca obiectele să înlocuiască această metodă cu una care poate face copii profunde.

Deci, în practică, ce înseamnă asta? Înseamnă că fiecare dintre clasele dvs. poate defini o metodă clonă care va face o copie profundă a acelui obiect. Înseamnă, de asemenea, că trebuie să scrieți o metodă clonă pentru fiecare clasă pe care o faceți.

Un truc: Marshalling

„Marshalling” a unui obiect este un alt mod de a spune „serializarea” unui obiect. Cu alte cuvinte, transformați acel obiect într-un flux de caractere care poate fi scris într-un fișier pe care îl puteți „dezmembra” sau „dezizualiza” mai târziu pentru a obține același obiect. Aceasta poate fi exploatată pentru a obține o copie profundă a oricărui obiect.

a = [[1,2]]
b = Marshal.load (Marshal.dump (a))
a [0] << 3
pune b.inspect

Ce s-a întâmplat aici? Marshal.dump creează un „dump” al tabloului cuibărit stocat în A. Acest dump este un șir de caractere binare destinat să fie stocat într-un fișier. Găzduiește conținutul complet al tabloului, o copie completă profundă. Următor →, Marshal.load face invers. Acesta analizează acest tablou de caractere binare și creează un Array complet nou, cu elemente complet noi de Array.

Dar acesta este un truc. Este ineficient, nu va funcționa la toate obiectele (ce se întâmplă dacă încercați să clonați o conexiune de rețea în acest fel?) Și probabil nu este teribil de rapid. Cu toate acestea, este cel mai ușor mod de a face copii adânci scurte initialize_copy sau clona metode. De asemenea, același lucru se poate face și cu metode precum to_yaml sau to_xml dacă aveți biblioteci încărcate pentru a le susține.

instagram story viewer