arr = ["red","green","yellow"]
arr2 = arr.clone
arr2[0].replace("blue")
puts arr.inspect
puts arr2.inspect
производит:
["blue", "green", "yellow"]
["blue", "green", "yellow"]
Должен там так или иначе сделать глубокую копию массива строк, кроме использования Маршала, поскольку я понимаю, что это - взлом.
Я мог сделать:
arr2 = []
arr.each do |e|
arr2 << e.clone
end
но это не кажется очень изящным, или эффективным.
Спасибо
Ваше второе решение можно сократить до arr2 = arr.map do |e| e.dup end
(если вам не нужно поведение clone
, рекомендуется использовать dup
).
В остальном ваши два решения являются стандартными решениями для выполнения глубокого копирования (хотя вторая версия является только одноуровневой (т.е. если вы используете ее для массива массивов строк, вы все еще можете мутировать строки)). На самом деле более удобного способа нет.
Edit: Вот рекурсивный метод deep_dup, который работает с произвольно вложенными массивами:
class Array
def deep_dup
map {|x| x.deep_dup}
end
end
class Object
def deep_dup
dup
end
end
class Numeric
# We need this because number.dup throws an exception
# We also need the same definition for Symbol, TrueClass and FalseClass
def deep_dup
self
end
end
Возможно, вы также захотите определить deep_dup для других контейнеров (например, Hash), иначе вы все равно получите неглубокую копию для них.
Я рекомендую вашу первоначальную идею, но написанную немного более сжато:
arr = ["red","green","yellow"]
arr2 = arr.inject([]) { |a,element| a << element.dup }
Вы можете использовать этот прием:
arr1 = %w{ red green blue }
arr2 = arr1.join("--!--").split("--!--")
Но это просто для развлечения :)
arr2[0].replace("lol")
p arr1
#=> ["red", "green", "blue"]
p arr2
#=> ["lol", "green", "blue"]
И он будет работать только для массивов 1 уровня