2009-09-23 12 views
5

(Kontynuacja mojego wcześniejszego pytania, Ruby: how can I copy a variable without pointing to the same object?)Ruby: jak mogę skopiować tę tablicę?

Piszę prosty program w języku Ruby, aby dokonać pewnych zmian w pliku .svg. Pierwszym krokiem jest pobranie informacji z pliku i umieszczenie go w tablicy. Aby nie czytać pliku z dysku za każdym razem, gdy wywoływana jest ta funkcja, próbuję użyć wzorca projektowego memoize - użyj buforowanego wyniku dla każdego połączenia po pierwszym.

Aby to zrobić, używam zmiennej globalnej, zdefiniowanej tuż przed funkcją. Ale nawet jeśli I .dup zmienną na lokalną przed zwróceniem zmiennej lokalnej, funkcja, która wywołuje tę, wciąż modyfikuje zmienną globalną.

Oto mój rzeczywisty kod:

#memoize to keep from having to read original file on each pass 
$svg_filedata_cache = [] #the global variable 
def svg_filedata(filename) 
    if $svg_filedata_cache.empty? 
     File.open(filename, "r"){|f| $svg_filedata_cache = f.readlines} 
    end 
    svg_filedata_cache = $svg_filedata_cache.dup #try to copy it 
    return svg_filedata_cache #SHOULD point to a different object (but doesn't) 
end 

dwa pytania (odpowiedź jednego lub obu):

  1. Dlaczego inne funkcje, które przyjmują i zmodyfikować wartość zwracana tutaj, również wpływa na zmienna globalna, mimo że użyłem .dup, aby ją skopiować?
  2. Jestem nowy w Ruby i jestem pewien, że nie jest to najbardziej rubinowy sposób na zrobienie tego (i nie lubię zmiennych globalnych). Czy możesz zaproponować lepszą strategię?
+0

P.S. Rozumiem, że naprawdę powinno to być $ svg_filedata_cache [filename], aby umożliwić wywoływanie funkcji za pomocą różnych nazw plików, ale nie jest to w tym przypadku potrzebne. –

+0

BTW, obiekty globalne i zwrócone mają inny object_id, myślę, że wymieniasz łańcuchy wewnątrz zwróconej tablicy, prawda? – khelll

+0

@khell - tak, oparłem moje stwierdzenie na tym, że zmieniano zawartość oryginalnej tablicy. –

Odpowiedz

9

Zmiana wprowadzonej tablicy nie wpłynie na oryginał. Jednak modyfikacje łańcuchów wewnątrz tablicy będą widoczne globalnie, ponieważ tablica globalna i tablica duplikowana nadal zawierają odwołania do tych samych ciągów (dup nie wykonuje głębokiej kopii).

Wykonaj głęboką kopię (svg_filedata_cache = $svg_filedata_cache.map {|line| line.dup}) lub po prostu unikaj operacji mutingu na strunach.

+1

Nie zdawałem sobie sprawy, że każdy ciąg w tablicy był jego własnym obiektem! Domyślam się, że to prawda, że ​​"WSZYSTKO w Rubin jest przedmiotem". :) –

6

podnosząca kod nieco:

$svg_filedata_cache = [] #the global variable 
def svg_filedata(filename) 
    # Use ||= for memoiziation 
    $svg_filedata_cache ||= File.open(filename, "r"){|f| $svg_filedata_cache = f.readlines} 
    $svg_filedata_cache.dup #shallow copying 
end 

Aktualizacja: prosty trik zrobić głębokie kopiowanie w ogóle jest:

def deep_copy(obj) 
    Marshal.load(Marshal.dump(obj)) 
end 
+0

Więc || = oznacza "jeśli lewa strona jest fałszywa (pusta), użyj prawej strony?" –

+2

oznacza to przypisanie wartości prawej strony do zmiennej po lewej stronie tylko wtedy, gdy zmienna ta nie jest już ustawiona. – khelll

2

Światowy prawdopodobnie nie jest modyfikowana, ale elementy że zmienia się to wraz z odniesieniem do pliku .dup. Aby uczynić go bardziej kanonicznym ruby, pozbądź się globalnego, użyj klasy i przeczytaj plik w funkcji initialize. (Konstruktor.) Spraw, aby tablica była zmienną instancji z @v.

Powiązane problemy