Изменение записи в массиве во время цикла - PullRequest
9 голосов
/ 21 февраля 2011
arr = ["red","green","blue","yellow"]

arr.each do |colour|
  if colour == "red"
    colour = "green"
  end
end

puts arr.inspect

Вышеприведенный код выводит:

["red", "green", "blue", "yellow"]

а почему бы и нет?

["green", "green", "blue", "yellow"]

Я думал, что цвет является ссылкой на текущий элемент в массиве, и что бы я ни сделал с ним, это повлияет на этот элемент массива?

Ответы [ 4 ]

5 голосов
/ 21 февраля 2011

Когда вы находитесь внутри блока arr.each, переменная colour привязана к одному из объектов в массиве arr.

Однако, как только вы сделаете присваивание colour = "green" в блоке теперь переменная colour связана с новым объектом (а именно с String со значением "green"), а исходное значение arr остается неизменным.

Один из способов достижения того, чтовы говорите:

arr.each_index do |i|
  arr[i] = "green" if arr[i] == "red"
end

, который управляет массивом напрямую.

4 голосов
/ 21 февраля 2011

Как правило, для преобразования массива в другой массив в Ruby вы не используете метод each, а вместо этого метод map (код пользователя sflinter выше):

arr.map { |x| x == "red" ? "green" : x }

Или, если быть более общим, учитывая карту преобразования:

mapping = {'red' => 'green', 'blue' => 'yellow'}

мы можем сделать что-то вроде

p arr.map {|e| mapping[e] || e} # => ["green", "green", "yellow", "yellow"]

PS: Как предложил Phrogz ниже, map создает новый экземпляр массива и, если вы планируете сохранить измененный массив в той же переменной, вам следует присвоить ему результат map.

С другой стороны, есть злой близнец map с именем map!, который изменяет исходный массив на месте (поэтому нет необходимости присваивать его обратно), экономя некоторое пространство и время, но нарушая чистый функционал концепция программирования, что функции не должны иметь побочных эффектов.

4 голосов
/ 21 февраля 2011

Цвет действительно изначально является ссылкой на текущий элемент в массиве, но с 'color = "green" он теперь ссылается на строку new . Старый оставлен без изменений. Для экспериментов попробуйте заменить

colour = "green"

с одним или несколькими из

colour.replace "green"
colour.capitalize! #not capitalize, which would give a new string again
colour << "ish"

Все они работают с существующей строкой, но не создают новую.

2 голосов
/ 21 февраля 2011

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

arr.each do |colour|
  colour.replace "green" if colour == "red"
end

p arr
#=> ["green", "green", "blue", "yellow"]
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...