Zachowanie wartości domyślnej Ruby hash
[[3]}przechodzę przez Ruby Koans i trafiłem na #41, który moim zdaniem jest taki:
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
Nie mógł zrozumieć zachowania, więc Wygooglowałem go i znalazłem dziwne zachowanie Rubiego podczas używania domyślnej wartości Hash, np. Hash.nowe ([]) , które ładnie odpowiedziały na pytanie.
Więc rozumiem, jak to działa, moje pytanie brzmi, dlaczego domyślna wartość, taka jak liczba całkowita, która jest zwiększana, nie zmienia się podczas użytkowania? Na przykład:
puts "Text please: "
text = gets.chomp
words = text.split(" ")
frequencies = Hash.new(0)
words.each { |word| frequencies[word] += 1 }
To zajmie wejście użytkownika i policzy ilość razy każde słowo jest używane, to działa, ponieważ domyślna wartość 0 jest zawsze używany.
Mam wrażenie, że ma to związek z operatorem<<
, ale chciałbym wyjaśnić. 3 answers
Inne odpowiedzi wydają się wskazywać, że różnica w zachowaniu wynika z tego, że Integer
są niezmienne i Array
są zmienne. Ale to jest mylące. Różnica nie polega na tym, że twórca Ruby postanowił uczynić jedną niezmienną, a drugą mutowalną. Różnica polega na tym, że ty, programista zdecydował się na mutację jednego, ale nie drugiego.
Nie chodzi o to, czy Array
s są mutowalne, tylko o to, czy ty mutujesz to.
Możesz uzyskać oba zachowania, które widzisz powyżej, po prostu za pomocą Array
S. Obserwuj:
Jeden domyślny Array
z mutacją
hsh = Hash.new([])
hsh[:one] << 'one'
hsh[:two] << 'two'
hsh[:nonexistent]
# => ['one', 'two']
# Because we mutated the default value, nonexistent keys return the changed value
hsh
# => {}
# But we never mutated the hash itself, therefore it is still empty!
Jeden domyślny Array
bez mutacji
hsh = Hash.new([])
hsh[:one] += ['one']
hsh[:two] += ['two']
# This is syntactic sugar for hsh[:two] = hsh[:two] + ['two']
hsh[:nonexistant]
# => []
# We didn't mutate the default value, it is still an empty array
hsh
# => { :one => ['one'], :two => ['two'] }
# This time, we *did* mutate the hash.
Nowy, inny Array
za każdym razem z mutacją
hsh = Hash.new { [] }
# This time, instead of a default *value*, we use a default *block*
hsh[:one] << 'one'
hsh[:two] << 'two'
hsh[:nonexistent]
# => []
# We *did* mutate the default value, but it was a fresh one every time.
hsh
# => {}
# But we never mutated the hash itself, therefore it is still empty!
hsh = Hash.new {|hsh, key| hsh[key] = [] }
# This time, instead of a default *value*, we use a default *block*
# And the block not only *returns* the default value, it also *assigns* it
hsh[:one] << 'one'
hsh[:two] << 'two'
hsh[:nonexistent]
# => []
# We *did* mutate the default value, but it was a fresh one every time.
hsh
# => { :one => ['one'], :two => ['two'], :nonexistent => [] }
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-04-23 12:09:58
Dzieje się tak dlatego, że Array
W Rubim jest obiektem mutowalnym, więc możesz zmienić jego stan wewnętrzny, ale {[1] }nie jest mutowalny. Więc kiedy zwiększasz wartość używając +=
wewnętrznie, otrzymujesz to (Załóżmy, że i
jest naszym odniesieniem do Fixnum
obiektu):
- get object referred by
i
- get it internal value (lets name it
raw_tmp
) - utwórz nowy obiekt o wewnętrznej wartości
raw_tmp + 1
- przypisanie referencji do utworzonego obiektu do
i
Więc jak widzisz, my stworzył nowy obiekt i i
odwołuje się teraz do czegoś innego niż na początku.
Z drugiej strony, gdy używamy Array#<<
działa to w ten sposób:
- get object referred by
arr
- do stanu wewnętrznego dołącza dany element
Więc jak widać jest to znacznie prostsze, ale może powodować pewne błędy. Jednym z nich masz w swoim pytaniu, innym jest wyścig wątków, gdy booth próbuje jednocześnie dołączyć 2 lub więcej elementów. Sometimes you może się skończyć tylko z niektórymi z nich i z thrashami w pamięci, gdy użyjesz +=
również na tablicach, pozbędziesz się obu tych problemów(lub przynajmniej zminimalizujesz wpływ).
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-12-02 00:47:19
Z doc Ustawienie wartości domyślnej ma następujące zachowanie:
Zwraca wartość domyślną, wartość, która byłaby zwracana przez HSH, gdyby klucz nie istniał w hsh. Zobacz także Hash:: new oraz Hash # default=.
Dlatego za każdym razem, gdy frequencies[word]
nie jest ustawione, wartość dla danego klucza jest ustawiona na 0.
Powodem rozbieżności między dwoma blokami kodu jest to, że tablice są mutowalne w Rubim, podczas gdy liczby całkowite nie.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-04-23 01:31:08