2016-03-31 2 views
0

У меня есть массив, заполненный строками по умолчанию, и я пытаюсь заменить часть символов в строке по умолчанию в случайной позиции.Модификация элемента массива ведет себя неправильно

Если я что-то вроде этого, я буду иметь все элементы массива изменены:

arr = ["*"] * 10 
arr[0][0..2] = "aaa" 
arr 
# => ["aaa", "aaa", "aaa", "aaa", "aaa", "aaa", "aaa", "aaa", "aaa", "aaa"] 

Но если я инициализировать массив по-другому, это работает:

(0..10).each.map {|i| arr[i] = "*"} 
arr[0][0..2] = "aaa" 
arr 
# => ["aaa", "*", "*", "*", "*", "*", "*", "*", "*", "*", "*"] 

Некоторые больше, инициализировать и все элементы такие же:

str = "*" 
(0..10).each.map {|i| arr[i] = str} 
arr[0][0..2] = "aaa" 
arr 
# => ["aaa", "aaa", "aaa", "aaa", "aaa", "aaa", "aaa", "aaa", "aaa", "aaa"] 

Вместо этого я сделал это, чтобы инициализировать его уникальными элементами:

str = "*" 
(0..10).each.map {|i| arr[i] = "#{str}" } 
arr[0][0..2] = "aaa" 
arr 
# => ["aaa", "*", "*", "*", "*", "*", "*", "*", "*", "*"] 

Что такое фон для такого поведения?

ответ

4

Массивы хранят ссылки на объекты. Когда вы инициализируете массив таким образом, вы получаете массив с десятью ссылками на одну и ту же строку. Затем вы изменяете строку.

arr = ['*']*3 
# => ["*", "*", "*"] 
arr.map &:object_id 
# => [70305424624600, 70305424624600, 70305424624600] 

Для контраста, таким образом, рубин выделяет новую строку для каждого элемента:

Array.new(3){ '*' }.map &:object_id 
# => [70184497001120, 70184497001060, 70184497001000] 
+0

прав, потому что должен использоваться конструктор объекта 'Array # new', поскольку он может обрабатывать значения по умолчанию. [Прочитать документацию] (http://ruby-doc.org/core-2.2.0/Array.html) – Charles

1

Когда вы делаете arr=["*"]*10, вы помещаете тот же самый объект String во все слоты массива. В то время как (0..10).each.map { |i| arr[i] = "*" } создает новый объект String для каждого элемента массива.

Illustrated со следующим кодом:

(0..10).each.map { |i| arr[i] = "*" } 
arr[0].equal? arr[1] # Check if first and second elements point to same Object 
# => false 
arr = ["*"] * 10 
arr[0].equal? arr[1] 
# => true 
0
arr = ["*"]*10 

Это утверждение создать массив из 10 элементов, но все эти элементы не являются уникальными и относятся к тот же экземпляр строки "*", которую вы создали перед заполнением массива. Я имею в виду, что ваш код такой же, как:

a = "*" 
arr = [a, a, a, a, a, a, a, a, a, a] 
#=> ["*", "*", "*", "*", "*", "*", "*", "*", "*", "*"] 

arr.map(&:object_id) 
#=> [15424420, 15424420, 15424420, 15424420, 15424420, 15424420, 15424420, 15424420, 15424420, 15424420] 

Все элементы массива относятся к одному экземпляру строки, так что при изменении любого значения элемента в массиве, то вы на самом деле изменить значение переменной , так что в результате вы получите тот же массив, заполненный переменной, но так как его значение было изменено на «ааа» то вывод будет выглядеть следующим образом:

["aaa", "aaa", "aaa", "aaa", "aaa", "aaa", "aaa", "aaa", "aaa", "aaa"] 

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

arr = (0..10).each.map { |i| arr[i] = "*" } 
#=> ["*", "*", "*", "*", "*", "*", "*", "*", "*", "*"] 
arr.map(&:object_id) 
#=> [14451520, 14451500, 14451480, 14451440, 14451420, 14451320, 14451300, 14451280, 14451260, 14451240, 14451160] 
+0

* Все эти элементы не уникальны * - вы имеете в виду, что они уникальны? – sawa

+0

Я имею в виду, что все элементы массива равны друг другу по значению и ссылке, поэтому они не уникальны. – SunnyMagadan