2012-02-18 25 views
45

yo estaba pasando por Ruby Koans tutorial series, cuando me encontré con esto en about_hashes.rb:¿Cómo funciona el operador de pala (<<) en Ruby Hashes?

def test_default_value_is_the_same_object 
    hash = Hash.new([]) 

    hash[:one] << "uno" 
    hash[:two] << "dos" 

    assert_equal ["uno", "dos"], hash[:one] 
    assert_equal ["uno", "dos"], hash[:two] 
    assert_equal ["uno", "dos"], hash[:three] 

    assert_equal true, hash[:one].object_id == hash[:two].object_id 
end 

Los valores en assert_equals, es en realidad lo que el tutorial de espera. Pero no pude entender cómo hay una diferencia entre usar el operador << y el operador =?

Mi expectativa era que:

  • hash[:one] habría ["uno"]
  • hash[:two] habría ["dos"]
  • hash[:three] habría []

Puede alguien explicarme por qué mi expectativa era malo?

+4

Gracioso, eso es exactamente lo que esperaba. Entonces, las montañas nuevamente eran simplemente montañas. –

Respuesta

52

Cuando está haciendo hash = Hash.new([]) está creando un hash cuyo valor predeterminado es la misma instancia Array para todas las claves. Entonces, cuando accedas a una clave que no existe, obtienes la misma Matriz.

h = Hash.new([]) 
h[:foo].object_id # => 12215540 
h[:bar].object_id # => 12215540 

Si desea una matriz por clave, usted tiene que utilizar la sintaxis del bloque de Hash.new:

h = Hash.new { |h, k| h[k] = [] } 
h[:foo].object_id # => 7791280 
h[:bar].object_id # => 7790760 

Editar: también ver lo que Gazler tiene que decir con respecto al método #<< y en qué objeto realmente lo está llamando.

+1

+1 para usar object_id para mostrar la referencia. – Gazler

+0

Lo tengo gracias. Entonces, la matriz vacía original es un objeto que se almacena como un valor predeterminado. Y seguimos recibiendo ese objeto original en lugar de un 'nil'. ¡Ordenado! Gracias a ambas respuestas (por usted y @Gazler), invirtiendo ambos, pero aceptando esto. – bits

58

Ha confundido la forma en que esto funciona un poco. En primer lugar, un Hash no tiene un método <<, ese método en su ejemplo existe en el conjunto.

La razón por la que su código no está cometiendo errores es porque está pasando un valor predeterminado a su hash a través del constructor. http://ruby-doc.org/core-1.9.3/Hash.html#method-c-new

hash = Hash.new([]) 

Esto significa que si no existe una clave, a continuación, se devolverá una matriz. Si ejecuta el siguiente código:

hash = {} 
hash[:one] << "uno" 

A continuación, obtendrá un error de método indefinido.

Así que en su ejemplo, lo que está sucediendo realmente es:

hash = Hash.new([]) 

hash[:one] << "uno" #hash[:one] does not exist so return an array and push "uno" 
hash[:two] << "dos" #hash[:two] does not exist, so return the array ["uno"] and push "dos" 

La razón de que no devuelve un array con un elemento cada vez que como se puede esperar, es porque almacena una referencia al valor que pasas al constructor Lo que significa que cada vez que se presiona un elemento, modifica la matriz inicial.

+2

Gracias @gazler. Esto ayudó a dejarlo en claro. – bits

+1

Muchas gracias señor – Etch

+4

Esta debería ser la respuesta aceptada. Una explicación mucho más clara.Gracias –

Cuestiones relacionadas