2012-07-16 21 views
5

Quiero hacer un seguimiento de los recuentos de algunas cadenas con nombre arbitrariamente y luego restablecer los recuentos a cero. Mi idea era hacer lo siguiente:Configuración de hash igual a otro hash en 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} 

...

Ahora quiero restablecer de nuevo a new_hash reset_hash:

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

lo que está pasando aquí? Parece que reset_hash en realidad se ha configurado en new_hash, que es lo contrario de lo que yo quería. ¿Cómo implemento el comportamiento deseado?

Respuesta

5

Cuando tiene una variable que apunta a un objeto, realmente solo tiene una referencia al objeto. Si tanto a como b apuntan al hash {1 => 3, "foo" => 54}, cambiar aob cambiará al otro.

Sin embargo, puede usar una combinación de dos métodos para hacer lo que desee.

un valor predeterminado para el hash:

new_hash = Hash.new(0) 

Esto da valores no utilizados un valor por defecto de 0:

new_hash["eggs"] # -> 0 

continuación, puede agregar cuenta:

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

Cuando 'Hecho simplemente llame al

new_hash.clear # => {} 

y el hachís se restablecerá, pero todavía nuevos accesos por defecto a 0.

Tenga en cuenta que si establece el valor predeterminado a un tipo de que no sea un número de objeto que puede ser capaz de cambiar las cosas, debido a todo el tema de referencia mencionado anteriormente.

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" 

Para evitar esto se puede pasar a un bloque que almohadilla:

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

Esto crea un nuevo objeto en la clave cada vez, por lo que los cambios en uno no se propagarán:

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

¡Gracias! Respuesta muy informativa. –

2

Está modificando un solo hash.

Ambas variables se refieren al mismo hash. Cuando cambia un elemento en el hash, ambas referencias reflejarán ese cambio – porque es la misma instinto hash.

Quizás quieras copiar el hash primero? Si lo haces, y tienes un hash con objetos complejos, también necesitarás investigar copias superficiales vs. copias profundas/clonación.

+0

Pensé que estaba copiando el hash. Entonces, ¿cómo hago eso correctamente? –

+0

Está copiando una * referencia * al hash; necesita usar ['clone' (docs)] (http://apidock.com/ruby/Object/clone), tomando nota de las advertencias. Hay muchas maneras de hacer un clon profundo, que incluye ordenar/desasignar el objeto. –

1

Debe usar clonar para hacer su copia.

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

De lo contrario, sólo se va a crear 2 "punteros" para el mismo hash, no copiar el contenido.

Luego use replace para copiar los contenidos clonados en su hash existente.

+0

Lo tengo. ¡Gracias! –

5

Como han mencionado otros, debe utilizar clone. Su tarea debe verse así:

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