2009-09-23 22 views
5

(Follow-up zu meiner früheren Frage, Ruby: how can I copy a variable without pointing to the same object?)Ruby: Wie kann ich dieses Array kopieren?

Ich schreibe ein einfaches Ruby-Programm, um einige Ersetzungen in einer SVG-Datei zu machen. Der erste Schritt besteht darin, Informationen aus der Datei zu ziehen und sie in ein Array einzufügen. Um bei jedem Aufruf dieser Funktion das Lesen der Datei von der Platte fernzuhalten, versuche ich das Memoize-Entwurfsmuster zu verwenden - verwende bei jedem Anruf nach dem ersten ein zwischengespeichertes Ergebnis.

Um dies zu tun, verwende ich eine globale Variable, definiert kurz vor der Funktion. Aber auch wenn ich diese Variable in eine lokale Variable umschreibe, bevor ich die lokale Variable zurückgebe, ändert die Funktion, die diese Variable aufruft, immer noch die globale Variable.

Hier ist meine eigentliche Code:

#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 

Zwei Fragen (entweder eine oder beide beantworten):

  1. Warum andere Funktionen, die in nehmen und ändern Sie den Wert hier zurückgekehrt, auch Auswirkungen auf die globale Variable, obwohl ich .dup verwendet habe, um es zu kopieren?
  2. Ich bin neu in Ruby und ich bin mir sicher, das ist nicht der Rubysteque Weg, dies zu tun (und ich mag keine globalen Variablen, sowieso). Können Sie eine bessere Strategie vorschlagen?
+0

P.S. Ich weiß, dass es wirklich $ svg_filedata_cache [Dateiname] sein sollte, um Funktionsaufrufe mit verschiedenen Dateinamen zu ermöglichen, aber das ist in diesem Fall nicht nötig. –

+0

BTW, die globale und die zurückgegebenen Objekte haben unterschiedliche object_id, ich glaube, Sie erwähnen die Zeichenfolgen innerhalb der zurückgegebenen Array, richtig? – khelll

+0

@ khell - Ja, ich habe meine Aussage auf die Tatsache gestützt, dass der Inhalt des ursprünglichen Arrays geändert wurde. –

Antwort

9

Das Ändern des überlagerten Arrays wirkt sich nicht auf das Original aus. Änderungen an den Strings innerhalb des Arrays werden jedoch global sichtbar, da das globale Array und das duplizierte Array weiterhin Verweise auf dieselben Strings enthalten (dup führt keine tiefe Kopie durch).

Führen Sie entweder eine tiefe Kopie (svg_filedata_cache = $svg_filedata_cache.map {|line| line.dup}) durch oder vermeiden Sie einfach Mutationsoperationen an den Strings.

+1

Ich erkannte nicht, dass jede Zeichenfolge im Array ein eigenes Objekt war! Ich denke, es ist wirklich wahr, dass "ALLES in Ruby ein Objekt ist." :) –

6

den Code ein wenig Verbesserung:

$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 

Update: ein einfacher Trick im allgemeinen tief Kopieren zu tun ist:

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

Also || = bedeutet "wenn die linke Seite falsch (leer) ist, benutze die rechte Seite?" –

+2

bedeutet, dass der rechte Seitenwert nur dann der Variablen auf der linken Seite zugewiesen wird, wenn diese Variable bereits gesetzt ist. – khelll

2

Die globale wird wahrscheinlich nicht modifiziert wird, sondern die Elemente dass es und deine .dup-Referenz sich ändern. Um es kanonischer zu machen, entferne das Globale, benutze eine Klasse und lies die Datei in der initialize Funktion. (Der Konstruktor.) Machen Sie das Array zu einer Instanzvariablen mit @v.

Verwandte Themen