Я расширю комментарий @pst:
почему это не работает?
arr.each { |v| v = "bad" }
Поскольку each
выполняет итерацию по массиву и помещает каждый элемент в блок, указанный вами как локальную переменную v
, поскольку v
не является ссылкой на массив arr
.
new_arr = arr.each { |v| v = "bad" }
each
не возвращает массив, для этого вы должны использовать map
(см. ответ @benjaminbenben). Поэтому его назначение не «работает».
arr.each { |v| arr[arr.index v] = "bad" }
Здесь вы помещаете каждый элемент в arr
в локальную переменную v
, но вы также ссылаетесь на сам массив в блоке, поэтому вы можете присваивать массиву и использовать локальную переменную v
для найдите индекс, соответствующий содержимому v
(но вы можете обнаружить, что это не сработает, как вы ожидаете, если элементы не все уникальны).
arr.each { |p| p.age = 50 }
kid.age #-> 50
Здесь вы снова заполнили локальную переменную p
каждым элементом/объектом в arr
, но затем вы получили доступ к каждому элементу через метод, поэтому вы можете изменить этот элемент - вы не изменяете массив< /сильный>. Это отличается, потому что ссылка относится к содержимому локальной переменной, которую вы перепутали со ссылкой на массив. Это отдельные вещи.
В ответ на комментарий ниже:
arr[0]
# => #<Person:0xf98298 @age=50>
Все дело в том, кто на кого когда ссылается.
Попробуй это:
v = Person.new
# => #<Person:0x000001008de248 @age=0>
w = Person.new
# => #<Person:0x000001008d8050 @age=0>
x = v
# => #<Person:0x000001008de248 @age=0>
v = Person.new
# => #<Person:0x00000100877e80 @age=0>
arr = [v,w,x]
# => [#<Person:0x00000100877e80 @age=0>, #<Person:0x000001008d8050 @age=0>, #<Person:0x000001008de248 @age=0>]
v
ссылается на 2 разных объекта. v
- это не фиксированная вещь, это имя. Сначала это относится к #<Person:0x000001008de248 @age=0>
, затем к #<Person:0x00000100877e80 @age=0>
.
Теперь попробуйте следующее:
arr.each { |v| v = "bad" }
# => [#<Person:0x00000100877e80 @age=0>, #<Person:0x000001008d8050 @age=0>, #<Person:0x000001008de248 @age=0>]
Все они являются объектами, но ничего не обновлялось и не «работало». Почему? Потому что при первом входе в блок v
относится к элементу в массиве, который был получен (дан). Итак, на первой итерации v
равно #<Person:0x00000100877e80 @age=0>
.
Но затем мы присваиваем "bad"
v
. Мы не присваиваем "bad"
первому индексу массива, потому что мы вообще не ссылаемся на массив. arr
— это ссылка на массив. Поместите arr
внутрь блока, и вы сможете изменить его:
arr.each { |v|
arr[0] = "bad" # yes, a bad idea!
}
Почему же тогда arr.each { |p| p.age = 50 }
обновляет элементы в массиве? Потому что p
относится к объектам, которые также оказались в массиве. В первой итерации p
ссылается на объект, также известный как kid
, а kid
имеет метод age=
, и вы вставляете в него 50
. kid
также является первым элементом в массиве, но вы говорите о kid
, а не о массиве. Вы можете сделать это:
arr.each { |p| p = "bad"; p.age }
NoMethodError: undefined method `age' for "bad":String
Сначала p
ссылался на объект, который также оказался в массиве (откуда он был получен), но затем p
стал ссылаться на "bad"
.
each
перебирает массив и возвращает значение на каждой итерации. Вы получаете только значение не массива. Если вы хотите обновить массив, вы либо делаете:
new_arr = arr.map{|v| v = "bad" }
new_arr = arr.map{|v| "bad" } # same thing
or
arr.map!{|v| v = "bad"}
arr.map!{|v| "bad"} # same thing
поскольку map
возвращает массив, заполненный возвращаемым значением блока. map!
обновит ссылку, по которой вы его вызвали, массивом, заполненным возвращаемым значением блока. Как правило, в любом случае обновлять объект при его повторении — плохая идея. Я считаю, что всегда лучше думать об этом как о создании нового массива, а затем вы можете использовать методы !
в качестве ярлыка.
person
iain
schedule
23.10.2012
v
— это локальная переменная, связанная с значением, которое было в индексе. Это не ссылка на оригинальный слот массива! - person   schedule 23.10.2012