2012-02-18 13 views
45

stavo attraversando Ruby Koans tutorial series, quando mi sono imbattuto in questo about_hashes.rb:Come funziona l'operatore di pala (<<) in Ruby Hash?

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 

I valori in assert_equals, è in realtà ciò che il tutorial previsto. Ma non riuscivo a capire come ci sia una differenza tra l'utilizzo dell'operatore << e l'operatore =?

La mia aspettativa era che:

  • hash[:one] sarebbe ["uno"]
  • hash[:two] sarebbe ["dos"]
  • hash[:three] sarebbe []

Qualcuno può spiegare perché la mia aspettativa era sbagliato?

+4

Divertente, è esattamente quello che mi aspettavo. Quindi, le montagne erano di nuovo solo montagne. –

risposta

52

Quando si esegue hash = Hash.new([]) si sta creando un hash il cui valore predefinito è esattamente la stessa istanza di array per tutte le chiavi. Quindi, ogni volta che si accede a una chiave che non esiste, si ottiene la stessa matrice.

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

Se si desidera un array per tasto, è necessario utilizzare la sintassi del blocco di Hash.new:

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

Edit: vedere anche cosa Gazler ha da dire per quanto riguarda il metodo di #<< e su quale oggetto si sta effettivamente chiamando.

+1

+1 per usare object_id per mostrare il riferimento. – Gazler

+0

Capito grazie. Quindi, l'array vuoto originale è un oggetto che viene memorizzato come valore predefinito. E continuiamo a recuperare quell'oggetto originale anziché un 'nil'. Neat! Grazie a entrambe le risposte (da parte tua e @ Gazler), facendo upvoting entrambi, ma accettando questo. – bits

58

Hai confuso il modo in cui questo funziona un po '. Prima di tutto, un hash non ha un metodo <<, quel metodo nell'esempio esiste sull'array.

Il motivo per cui il codice non sta commettendo errori è perché si passa un valore predefinito al proprio hash tramite il costruttore. http://ruby-doc.org/core-1.9.3/Hash.html#method-c-new

hash = Hash.new([]) 

Ciò significa che se una chiave non esiste, allora restituirà un array. Se si esegue il seguente codice:

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

Quindi si otterrà un errore di metodo non definito.

Quindi nel tuo esempio, ciò che sta realmente accadendo è:

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 ragione per cui non restituisce un array con un elemento ogni volta come ci si potrebbe aspettare, è perché memorizza un riferimento al valore che si passa attraverso il costruttore. Significa che ogni volta che un elemento viene premuto, modifica l'array iniziale.

+2

Grazie a @gazler. Ciò ha contribuito a chiarire. – bits

+1

Grazie mille signore – Etch

+4

Questa dovrebbe essere la risposta accettata. Una spiegazione molto più chiara.Grazie –

Problemi correlati