Дублирование массива строк Ruby - PullRequest
10 голосов
/ 05 апреля 2010
arr = ["red","green","yellow"]

arr2 = arr.clone
arr2[0].replace("blue")

puts arr.inspect
puts arr2.inspect

производит:

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

Есть ли в любом случае сделать глубокую копию массива строк, кроме использования Marshal, как я понимаю, это хак.

Я мог бы сделать:

arr2 = []
arr.each do |e|
  arr2 << e.clone
end

но это не кажется очень элегантным или эффективным.

Спасибо

Ответы [ 6 ]

10 голосов
/ 05 апреля 2010

Ваше второе решение может быть сокращено до 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), в противном случае вы все равно получите поверхностную копию для них.

5 голосов
/ 14 октября 2015

Я в подобной ситуации и очень обеспокоен скоростью. Самый быстрый способ для меня - использовать map{&:clone}

Так попробуйте это:

pry(main)> a = (10000..1000000).to_a.shuffle.map(&:to_s)
pry(main)> Benchmark.ms { b = a.deep_dup }                                                                                     
=> 660.7760030310601
pry(main)> Benchmark.ms { b = a.join("--!--").split("--!--") }
=> 605.0828141160309
pry(main)> Benchmark.ms { b = a.map(&:clone) }
=> 450.8283680770546
5 голосов
/ 06 апреля 2010

Я рекомендую вашу первоначальную идею, но написано несколько более кратко:

arr = ["red","green","yellow"]
arr2 = arr.inject([]) { |a,element| a << element.dup }
3 голосов
/ 24 августа 2016

Вы можете сделать глубокую копию массива a с помощью следующего кода:

 Marshal.load(Marshal.dump(a))
2 голосов
/ 06 января 2017

Это выглядит так просто ... Просто запустите приведенный ниже код:

a = [1,2,3]
b = [].replace(a)

b[1] = 5

puts a
puts b

Запустите приведенный выше код, и вы заметите разницу.Ура!

1 голос
/ 05 апреля 2010

Вы можете использовать этот хак:

arr1 = %w{ red green blue }
arr2 = arr1.join("--!--").split("--!--")

Но это просто для удовольствия:)

arr2[0].replace("lol")
p arr1
#=> ["red", "green", "blue"]
p arr2
#=> ["lol", "green", "blue"]

И это будет работать только для массивов 1 уровня

...