2012-07-16 13 views
5

Ich möchte die Zählungen einiger willkürlich benannten Strings verfolgen und dann die Zähler auf Null zurückgesetzt. Mein Gedanke war, folgendes zu tun:Einstellung Hash gleich einem anderen Hash in Ruby

reset_hash={"string1"=>0,"string2"=>0,"string3"=>0} 
=> {"string1"=>0, "string2"=>0, "string3"=>0} 

new_hash = reset_hash 
=> {"string1"=>0, "string2"=>0, "string3"=>0} 

new_hash["string1"]=1 
new_hash["string3"]=1 
new_hash 
=> {"string1"=>1, "string2"=>0, "string3"=>1} 

...

Jetzt möchte ich zurückgesetzt new_hash zurück zu reset_hash:

new_hash = reset_hash 
=> {"string1"=>1, "string2"=>0, "string3"=>1} 
reset_hash 
=> {"string1"=>1, "string2"=>0, "string3"=>1} 

Was ist hier los? Es scheint, dass reset_hash tatsächlich auf new_hash gesetzt wurde, was das Gegenteil von dem ist, was ich wollte. Wie implementiere ich das gewünschte Verhalten?

Antwort

5

Wenn Sie eine Variable auf ein Objekt zeigen, haben Sie wirklich nur einen Verweis auf das Objekt. Wenn sowohl a als auch b auf den Hash {1 => 3, "foo" => 54} zeigen, ändert sich die Änderung von a oder b.

Sie können jedoch eine Kombination von zwei Methoden verwenden, um zu machen, was Sie wollen.

Ein Standardwert für den hash:

new_hash = Hash.new(0) 

Dies gibt nicht verwendeten Werte ein Standardwert von 0:

new_hash["eggs"] # -> 0 

können Sie dann zählt hinzu:

new_hash["string1"] += 1 # => 1 

Wenn Sie 're getan, rufen Sie einfach

new_hash.clear # => {} 

und Ihre Hash wird zurückgesetzt, aber neue Zugriffe auf 0

Hinweis wird standardmäßig immer noch, dass Sie in der Lage sein kann, wenn Sie den Standardwert auf eine Art von Objekt, das nicht eine Zahl festgelegt, Dinge zu ändern aufgrund das ganze oben erwähnte Referenzthema.

irb(main):031:0> b = Hash.new("Foo") #=> {} 
irb(main):032:0> b[3] #=> "Foo" 
irb(main):033:0> b[33] #=> "Foo" 
irb(main):034:0> b[33].upcase! #=> "FOO" 
irb(main):035:0> b[3] # => "FOO" 

Um dies zu umgehen können Sie einen Block passieren Sie hash:

h = Hash.new {|hash, key| hash[key] = "new default value"} 

Dies erzeugt ein neues Objekt in jedem Schlüssel, so dass Änderungen an einer nicht kräuseln:

d = Hash.new{ |hash,key| hash[key] = "string"} #=> {} 
d[3] # => "string" 
d[3].upcase! #=> "STRING" 
d[5] #=> "string" 
+0

Danke! Sehr informative Antwort. –

2

Sie ändern einen einzelnen Hash.

Beide Variablen beziehen sich auf den gleichen Hash. Wenn Sie ein Element im Hash ändern, werden beide Referenzen diese Änderung widerspiegeln –, weil es die gleiche Hash-Instanz ist.

Vielleicht möchten Sie den Hash zuerst kopieren? Wenn Sie dies tun, und Sie einen Hash mit komplexen Objekten haben, müssen Sie auch seichte vs. tiefe Kopien/Klonen untersuchen.

+0

Ich dachte, ich kopiert den Hash. Wie mache ich das richtig? –

+0

Sie kopieren eine * Referenz * in den Hash; Sie müssen ['clone' (docs)] (http://apidock.com/ruby/Object/clone) verwenden und die Vorbehalte notieren. Es gibt viele Möglichkeiten, einen tiefen Klon zu erstellen, einschließlich des Marshalling/Unmarshalling des Objekts. –

1

Sie müssen Klon verwenden, um Ihre Kopie zu erstellen.

Siehe https://stackoverflow.com/a/4157438/1118101

Ansonsten sind Sie nur die Schaffung 2 „Zeiger“ auf den gleichen Hash, nicht um den Inhalt zu kopieren.

Verwenden Sie dann ersetzen, um den geklonten Inhalt wieder in Ihren vorhandenen Hash zu kopieren.

+0

Verstanden. Vielen Dank! –

5

Wie bereits erwähnt, müssen Sie clone verwenden. Ihre Aufgabe sollte wie folgt aussehen:

reset_hash={"string1"=>0,"string2"=>0,"string3"=>0} 
new_hash = reset_hash.clone 

new_hash["string1"]=1 
new_hash["string3"]=1 
new_hash 

new_hash = reset_hash.clone 
reset_hash