使用 .replace制作数组的副本

我有一个类,我在实例变量上使用Array#shift实例方法。 我以为我对我的实例变量进行了“复制”,但实际上我没有和shift实际上是在改变实例变量。

例如,在我预期得到["foo", "bar", "baz"]两次之前给出以下内容:

 class Foo attr_reader :arr def initialize arr @arr = arr end def some_method foo = arr foo.shift end end foo = Foo.new %w(foo bar baz) p foo.arr #=> ["foo", "bar", "baz"] foo.some_method p foo.arr #=> ["bar", "baz"] 

结果:

 ["foo", "bar", "baz"] ["bar", "baz"] 

但如图所示,我的“副本”根本不是真正的副本。 现在,我不确定我是否应该调用我想要的“复制”,“克隆”,“重复”,“深度克隆”,“深度复制”,“冻结克隆”等等…

我真的很困惑要搜索什么,并发现了一堆疯狂的尝试,似乎“制作一个数组的副本”。

然后我找到另一个答案 ,字面上一行解决了我的问题:

 class Foo attr_reader :arr def initialize arr @arr = arr end def some_method foo = [].replace arr foo.shift end end foo = Foo.new %w(foo bar baz) p foo.arr #=> ["foo", "bar", "baz"] foo.some_method p foo.arr #=> ["foo", "bar", "baz"] 

输出:

 ["foo", "bar", "baz"] ["foo", "bar", "baz"] 

我知道Array#replace是一个在Array#replace实例上调用的实例方法恰好是一个空数组(例如foo = ["cats", "and", "dogs"].replace arr仍然可以工作)我得到实例变量@arr的“副本”是@arr

但这有什么不同于:

 foo = arr foo = arr.clone foo = arr.dup foo = arr.deep_clone Marshal.load # something something # etc... 

或者任何其他疯狂的dupmap组合和inject我在SO上看到的?

这是ruby中可变性的棘手概念。 就核心对象而言,这通常会产生数组和散列。 字符串也是可变的,但可以通过脚本顶部的标志禁用。 请参阅注释“frozen_string_literal:true”的作用是什么? 。

在这种情况下,您可以轻松调用dupdeep_dupclone以达到与replace相同的效果:

 ['some', 'array'].dup ['some', 'array'].deep_dup ['some', 'array'].clone Marshal.load Marshal::dump(['some', 'array']) 

在差异方面, dupclone是相同的,除了一些细微的细节 – 请参阅Ruby的dup和clone方法之间的区别是什么?

这些和deep_dup之间的区别在于deep_dup递归工作。 例如,如果复制嵌套数组,则不会克隆内部数组:

  a = [[1]] b = a.clone b[0][0] = 2 a # => [[2]] 

哈希也会发生同样的事情。

Marshal.load Marshal::dump 是深度克隆对象的一般方法,与deep_dup不同,它位于ruby核心。 Marshal::dump返回一个字符串,因此在将对象序列化为文件时可以很方便。

如果你想避免像这样的意外错误,请保留哪些方法有副作用的心理索引,只在有意义的时候调用它们。 方法名称末尾的解释点表示它有副作用,但其他包括unshift,push,concat,delete和pop。 function编程的很大一部分是避免副作用。 你可以看到https://www.sitepoint.com/functional-programming-techniques-with-ruby-part-i/

首选方法是dup

  • 每当需要复制数组时都使用array.dup
  • 每当需要复制2D数组时,请使用array.map(&:dup)

除非您真的想深度复制整个对象图,否则不要使用编组技巧。 通常,您只想复制数组,而不是复制包含的元素。