复制一个由字符串组成的 Ruby 数组

11
arr = ["red","green","yellow"]

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

puts arr.inspect
puts arr2.inspect

生成:

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

是否有其他方法可以深度复制字符串数组,除了使用Marshal作为hack的方式?

我可以这样做:

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

但这似乎不太优雅或高效。

谢谢


你可以使用更短的内联块:arr.each{|e| arr2 << e.dup} - fl00r
6个回答

13

你的第二个解决方案可以简化为arr2 = arr.map do |e| e.dup end(除非你实际需要clone的行为,否则建议使用dup代替)。

除此之外,你的两种解决方案基本上是执行深拷贝的标准方式(尽管第二个版本仅为一级深度(即如果你将其用于字符串数组的数组,则仍然可以改变字符串的值)。)。没有更好的方法。

编辑:这里是一个递归的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,否则对于这些容器,你仍将得到浅层复制。


@Jon:不,你也可以定义一个递归的deep_dup方法(请参见我的编辑),但通常使用Marshal更容易。 - sepp2k
你能不能通过在Object中定义deep_dup来避免修补Numeric等内容?:respond_to?(:dup) ? dup : self - Lars Haugseth
@Lars:不,因为Numerics会响应dup。只是它们会通过抛出异常来响应。 - sepp2k
如果您的数组包含像整数这样的FixNums,则DUP存在问题-无法复制Fixnum。https://dev59.com/mUvSa4cB1Zd3GeqPawGT https://dev59.com/infZa4cB1Zd3GeqPTJLJ - stack1
@stack1 这就是为什么我定义了 deep_dup 方法返回 self 而不是对数字调用 dup 方法。 - sepp2k
显示剩余3条评论

7

我和你处于类似的情况,并非常关注速度。对于我来说最快的方法是利用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
我建议您保留最初的想法,但是用更简洁的语言来表达:

我建议您保留最初的想法,但用更简练的语言来表达:

arr = ["red","green","yellow"]
arr2 = arr.inject([]) { |a,element| a << element.dup }

1
如果您的数组包含像整数这样的FixNums,则DUP存在问题-无法复制Fixnum。https://dev59.com/mUvSa4cB1Zd3GeqPawGT https://dev59.com/infZa4cB1Zd3GeqPTJLJ - stack1

3
您可以通过以下代码深拷贝数组a
 Marshal.load(Marshal.dump(a))

2

看起来很简单...只需运行以下代码:

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

b[1] = 5

puts a
puts b

运行以上代码,您会注意到差异。干杯!

1
你可以使用这个hack:
arr1 = %w{ red green blue }
arr2 = arr1.join("--!--").split("--!--")

但这只是为了好玩 :)

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

它只适用于一级数组


1
它也只能在数组仅包含字符串且没有任何字符串包含“--!--”子字符串的情况下才能工作。 - sepp2k
sepp2k,是的,它只是为了一个目的而进行的黑客攻击 :) 就像 Tetra Pack 一样。 - fl00r

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接