2017-01-18 4 views
1

Пример:Почему добавление значения в массив в хеше также изменит другую переменную, которую он назначил? Код

hash_of_array = { a: [] } 
b = hash_of_array 
c = hash_of_array 

b[:a] << 1 

puts b # => { a: [1] } 
puts C# => { a: [1] } 

Почему как переменная имеет 1 в своем массиве? Я только добавил его на переменную b.

Пробовал на

  • Рубин версия 2.3.1, 2.4.0
  • Rbenv
+1

[может быть полезным] (http://stackoverflow.com/questions/1465569/ruby-how-can-i-copy -a-variable-without-pointing-to-the-same-object) –

ответ

3

Поскольку значение b и значение c и тот же объект (проверить Object#object_id) :

b.object_id == c.object_id 
#=> true 
0

Все объекты в рубине назначаются по ссылке, за исключением True, False, Nil и Fixnums, которые обрабатываются специально.

Вы можете проверить это, глядя на object_id

irb(main):001:0> a = [1,2,3] 
=> [1, 2, 3] 
irb(main):002:0> b = a 
=> [1, 2, 3] 
irb(main):003:0> a.object_id == b.object_id 
=> true 

Чтобы создать копию объекта, используйте .dup.

irb(main):005:0> b = a.dup 
=> [1, 2, 3] 
irb(main):006:0> a.object_id == b.object_id 
=> false 

Для примера, вам нужно глубокое копирование, так что вы можете сделать

Marshal.load(Marshal.dump(hash)) 

Итак,

irb(main):037:0> a = {a: []} 
=> {:a=>[]} 
irb(main):038:0> b = Marshal.load(Marshal.dump(a)) 
=> {:a=>[]} 
irb(main):039:0> b[:a] << 1 
=> [1] 
irb(main):040:0> b 
=> {:a=>[1]} 
irb(main):041:0> a 
=> {:a=>[]} 
+0

Как бы вы использовали 'dup' в примере OP? – Stefan

+0

Вам понадобится Rails 'deep_dup' –

0

Как уже упоминалось в начале, отличные ответы, проблема вы столкнулись, что a и b являются одним и тем же объектом. Другой ответ (позже удаленный) рекомендуется использовать с использованием метода dup, если достаточно мелкой копии. Проблема в том, что в ваших сценариях мелкая копия НЕ ДОЛЖНА.

Я предлагаю попробовать драгоценный камень full_dup. Этот камень добавляет новый метод, называемый full_dup. Full_dup выполняет глубокую копию ваших объектов. Так что с учетом вашего примера:

require 'full_dup' 

hash_of_array = { a: [] } 
b = hash_of_array.full_dup 
c = hash_of_array.full_dup 

b[:a] << 1 

puts b # => { a: [1] } 
puts C# => { a: [] } 

Как в стороне, full_dup не является демоном скорости. Вы должны использовать обычный dup, когда этот метод вам нужен.

Oops; Забыл упомянуть. Я являюсь автором полноцветной жемчужины.

+0

Это нормально рекомендовать вашу собственную библиотеку, но вы должны раскрыть свою принадлежность. – Stefan

+0

Почему не '.dup' достаточно? Что делает 'full_dup', что делает его уместным здесь? «Вы должны использовать обычный dup, когда этот метод вам нужен». - когда это? –

+0

Метод dup выполняет мелкую копию. Метод full_dup выполняет глубокую копию. Мелкие копии быстрее, чем глубокие копии, но они ... мелкие. Это все сказано в моем ответе. Я мог бы добавить, что мелкая копия делает копию объекта-контейнера, но не его содержимое. Глубокая копия также копирует контейнерный объект и его содержимое, а также их содержимое и их содержимое и т. Д. Для глубокого, как это необходимо. Как и следовало ожидать, это занимает довольно много времени, и его следует избегать, если это не нужно. Когда это необходимо? Вам нужно понять это и судить. –

5

Я знаю человека по имени Джон. Некоторые называют его Джони. Сегодня Джонни застрелили. Почему Джон тоже выстрелил?


foo = _something_ 

Просто дает альтернативное название foo к _something_. Он не клонирует его и не делает никаких других махинаций.

+3

Upvote для темной комедии. –

+3

Приобретено выше и слово «shenanigans» :) –

+1

Мне тяжело заниматься виселицей юмора, так как мой брат, Джон, тоже мертв. –

0

В вашем примере и, как упоминалось, @AndreyDeineko, b и c в основном это тот же объект.

Не обязательно показывать поведение, с которым вы смущены.

Что важно то, что они содержат ссылку на тот же массив:

array = [] 

b = { b: array } 
c = { c: array } 

puts b == c 
#=> false 

puts b.object_id == c.object_id 
#=> false 

puts b[:b].object_id == c[:c].object_id 
#=> true 

b[:b] << 'test' 

p b 
# {:b=>["test"]} 
p c 
# {:c=>["test"]}