2012-07-16 5 views
5

Voglio tenere traccia dei conteggi di alcune stringhe chiamate arbitrariamente e quindi azzerare i conteggi. Il mio pensiero è stato quello di effettuare le seguenti operazioni:Impostazione dell'hash uguale a un altro 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} 

...

Ora voglio riportare new_hash tornare a reset_hash:

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

cosa sta succedendo qui? Sembra che reset_hash sia stato effettivamente impostato su new_hash, che è l'opposto di quello che volevo. Come implementare il comportamento desiderato?

risposta

5

Quando si ha una variabile che punta a un oggetto, si ha solo un riferimento all'oggetto. Se sia aeb che puntano all'hash {1 => 3, "foo" => 54}, cambiando aob l'altro cambierà.

Tuttavia, è possibile utilizzare una combinazione di due metodi per ottenere ciò che si desidera.

un valore predefinito per l'hash:

new_hash = Hash.new(0) 

Questo dà valori inutilizzati un valore predefinito di 0:

new_hash["eggs"] # -> 0 

è quindi possibile aggiungere i conteggi:

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

Quando si basta chiamare il

new_hash.clear # => {} 

e il vostro hash sarà ripristinato, ma i nuovi accessi sarà ancora di default a 0.

Si noti che se si imposta il valore di default per un tipo di oggetto diverso da un numero che si può essere in grado di cambiare le cose a causa di l'intero problema di riferimento sopra riportato.

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" 

Per aggirare il problema è possibile passare un blocco a voi hash:

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

Questo crea un nuovo oggetto a chiave ogni volta, così i cambiamenti ad una non si incresparsi:

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

Grazie! Risposta molto informativa –

2

Stai modificando un singolo hash.

Entrambe le variabili si riferiscono allo stesso hash. Quando si modifica un elemento nell'hash, entrambi i riferimenti rifletteranno tale modifica – perché è la stessa istanza di hash.

Forse si desidera copiare prima l'hash? Se lo fai, e hai un hash con oggetti complessi, dovrai anche investigare su copie poco profonde o profonde/clonazione.

+0

Ho pensato che stavo copiando l'hash. Quindi, come faccio a farlo correttamente? –

+0

Stai copiando un * riferimento * all'hash; devi usare ['clone' (docs)] (http://apidock.com/ruby/Object/clone), notando gli avvertimenti. Ci sono molti modi per fare un clone profondo, incluso il solo marshalling/unmarshalling dell'oggetto. –

1

È necessario utilizzare il clone per creare la copia.

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

In caso contrario, si sta creando solo 2 "puntatori" per lo stesso hash, non copiare il contenuto.

Quindi utilizzare Sostituisci per copiare nuovamente il contenuto clonato nell'hash esistente.

+0

Capito. Grazie! –

5

Come già menzionato, è necessario utilizzare clone. Il tuo compito dovrebbe assomigliare a questo:

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