Jak robić głębokie kopie w Ruby
Yuri Arcurs/Getty Images
Często konieczne jest wykonanie kopii wartość w rubinach . Chociaż może się to wydawać proste i dotyczy prostych obiektów, gdy tylko będziesz musiał wykonać kopię struktury danych z wieloma tablicami lub skrótami na tym samym obiekcie, szybko odkryjesz wiele pułapek.
Przedmioty i referencje
Aby zrozumieć, co się dzieje, spójrzmy na prosty kod. Po pierwsze, operator przypisania używający POD (zwykłe stare dane) wpisze w Rubin .
a = 1
b = a
a += 1
stawia b
Tutaj operator przypisania tworzy kopię wartości a i przypisując go do b za pomocą operatora przypisania. Wszelkie zmiany w a nie znajdzie odzwierciedlenia w b . Ale co z czymś bardziej złożonym? Rozważ to.
a = [1,2]
b = a
a<< 3
stawia b.sprawdź
Przed uruchomieniem powyższego programu spróbuj zgadnąć, jaki będzie wynik i dlaczego. To nie to samo, co w poprzednim przykładzie, zmiany wprowadzone do a znajdują odzwierciedlenie w b , ale dlaczego? Dzieje się tak, ponieważ Szyk obiekt nie jest typem POD. Operator przypisania nie tworzy kopii wartości, po prostu kopiuje odniesienie do obiektu Array. The a oraz b zmienne są teraz Bibliografia do tego samego obiektu Array, wszelkie zmiany w jednej zmiennej będą widoczne w drugiej.
A teraz możesz zobaczyć, dlaczego kopiowanie nietrywialnych obiektów z odniesieniami do innych obiektów może być trudne. Jeśli po prostu tworzysz kopię obiektu, po prostu kopiujesz odniesienia do głębszych obiektów, więc twoja kopia jest określana jako „płytka kopia”.
Co zapewnia Ruby: dup i klon
Ruby udostępnia dwie metody tworzenia kopii obiektów, w tym jedną, którą można wykonać w celu wykonania głębokich kopii. The Obiekt#duplikat metoda utworzy płytką kopię obiektu. Aby to osiągnąć, dupa metoda wywoła inicjalizuj_kopiuj metoda tej klasy. To, co dokładnie robi, zależy od klasy. W niektórych klasach, takich jak Array, zainicjuje nową tablicę z tymi samymi członkami, co tablica oryginalna. Nie jest to jednak głęboka kopia. Rozważ następujące.
a = [1,2]
b = a.dup
a<< 3
stawia b.sprawdź
a = [ [1,2] ]
b = a.dup
a[0]<< 3
stawia b.sprawdź
Co się tutaj wydarzyło? The Array#initialize_copy Metoda rzeczywiście utworzy kopię Array, ale ta kopia sama w sobie jest płytką kopią. Jeśli masz w swojej tablicy inne typy, które nie są POD, użyj dupa będzie tylko częściowo głęboką kopią. Będzie tylko tak głęboka jak pierwsza tablica, głębsza tablice , haszy lub inne obiekty zostaną skopiowane tylko płytko.
Warto wspomnieć o jeszcze jednej metodzie, klon . Metoda klonowania robi to samo, co dupa z jednym ważnym rozróżnieniem: oczekuje się, że obiekty zastąpią tę metodę metodą, która może wykonywać głębokie kopie.
Więc w praktyce co to oznacza? Oznacza to, że każda z twoich klas może zdefiniować metodę klonowania, która utworzy głęboką kopię tego obiektu. Oznacza to również, że musisz napisać metodę klonowania dla każdej tworzonej klasy.
Sztuczka: marshalling
„Uszeregowanie” obiektu to kolejny sposób na powiedzenie „serializacji” obiektu. Innymi słowy, przekształć ten obiekt w strumień znaków, który można zapisać do pliku, który można później „odszeregować” lub „odserializować”, aby uzyskać ten sam obiekt. Można to wykorzystać do uzyskania głębokiej kopii dowolnego obiektu.
a = [ [1,2] ]
b = Marshal.load( Marshal.dump(a) )
a[0]<< 3
stawia b.sprawdź
Co się tutaj wydarzyło? Marszałek.dump tworzy „zrzut” zagnieżdżonej tablicy przechowywanej w a . Ten zrzut to binarny ciąg znaków przeznaczony do przechowywania w pliku. Zawiera pełną zawartość tablicy, kompletną głęboką kopię. Następny, Marshal.load robi coś przeciwnego. Analizuje tę binarną tablicę znaków i tworzy zupełnie nową tablicę Array z zupełnie nowymi elementami Array.
Ale to jest sztuczka. Jest nieefektywny, nie zadziała na wszystkich obiektach (co się stanie, jeśli spróbujesz w ten sposób sklonować połączenie sieciowe?) i prawdopodobnie nie jest strasznie szybki. Jest to jednak najłatwiejszy sposób na wykonanie głębokich kopii bez niestandardowych inicjalizuj_kopiuj lub klon metody. To samo można zrobić za pomocą metod takich jak to_yaml lub do_xml jeśli masz załadowane biblioteki do ich obsługi.