В Ruby все переменные являются указателями на объекты, то есть массивы, хэши, строки, целые числа.
Взгляните на этот пример из одного из упражнений Launch School:
name = ‘Bob’ save_name = name name = ‘Alice’ puts name, save_name
Посмотрите сначала на первые 2 строки. В первой строке инициализируется новая переменная (имя) для ссылки на строковый объект «Боб».
В следующей строке save_name указывает на имя переменной. Но помните: все переменные являются указателями на объекты. Следовательно, save_name также указывает на объект. Вопрос какой объект?
save_name указывает на какое имя. На что указывает имя? Строковый объект «Боб». Итак, save_name также указывает на «Боба».
name и save_name указывают на один и тот же объект.
Глядя теперь на третью строку, имя теперь переназначается на «Алиса». name теперь указывает на другое место в памяти. Обратите внимание, что исходная переменная имени все еще существует, только с другим идентификатором объекта. Кроме того, на исходный объект, который был переназначен, теперь нельзя ссылаться по «имени».
Итак, следующий вопрос: на какой объект теперь указывает save_name? Мы знаем, что имя было переназначено. Но относится ли save_name ко второму или первому объекту? Итак, мы только что сказали, что исходная переменная name все еще существует и не была изменена. Поэтому save_name указывает на исходную переменную имени «Боб».
Таким образом, имя возвращает «Алиса». И ставит save_name возвращает «Боб».
Теперь взгляните на это, еще одно из упражнений Launch School:
name = ‘Bob’ save_name = name name.upcase! puts name, save_name
Во второй строке выполняется деструктивный вызов метода для имени переменной. Мы знаем, что деструктивные вызовы методов мутируют или постоянно изменяют вызывающую программу, то есть исходную переменную. Так что в этом случае вызов upcase! постоянно изменяет имя. Так что теперь имя «БОБ». Так что save_name тоже «BOB».
Следовательно
puts name, save_name
возвращается
“BOB” “BOB”
Теперь возьмем этот пример.
array1 = %w(Moe Larry Curly Chemp Harpo Chico Groucho Zeppo) array2 = [] array1.each { |value| array2 << value } array1.each { |value| value.upcase! if value.start_with?(‘C’) } puts array2
Здесь array1 указывает на массив строк. array2 указывает на пустой массив.
В третьей строке мы перебираем каждый элемент массива1 и выполняем деструктивный вызов метода для массива2 с каждой итерацией. В частности, мы добавляем каждый элемент массива1 в массив2.
А вот и сложная часть.
В четвертой строке мы снова перебираем каждый элемент массива1 и выполняем деструктивный вызов метода для каждого элемента массива1. В частности, мы постоянно прописываем каждое слово, начинающееся с буквы C.
Вопрос в том, что делает вывод array2? Чтобы свести к минимуму путаницу, давайте сначала обновим array1 и array2.
array1 = %w(Moe Larry Curly Chemp Harpo Chico Groucho Zeppo) array2 = %w(Moe Larry Curly Chemp Harpo Chico Groucho Zeppo)
Просто смотреть на вышесказанное обманчиво. Потому что вы могли бы подумать, что массив2, хотя и имеет то же содержимое, что и массив1, отделен от массива1, так что если вы измените массив1, это изменение не повлияет на массив2. Но ответ не так прост.
array2 на самом деле содержит те же строковые объекты, что и array1, т. е. они ссылаются или указывают на одни и те же строковые объекты. Это не новые копии этих строковых объектов. На самом деле это новые ссылки на исходные строковые объекты. Это объясняет, почему все, что происходит с исходными строковыми объектами, теперь повлияет на новые ссылки.