使用 .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...
或者任何其他疯狂的dup
和map
组合和inject
我在SO上看到的?
这是ruby中可变性的棘手概念。 就核心对象而言,这通常会产生数组和散列。 字符串也是可变的,但可以通过脚本顶部的标志禁用。 请参阅注释“frozen_string_literal:true”的作用是什么? 。
在这种情况下,您可以轻松调用dup
, deep_dup
, clone
以达到与replace
相同的效果:
['some', 'array'].dup ['some', 'array'].deep_dup ['some', 'array'].clone Marshal.load Marshal::dump(['some', 'array'])
在差异方面, dup
和clone
是相同的,除了一些细微的细节 – 请参阅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)
除非您真的想深度复制整个对象图,否则不要使用编组技巧。 通常,您只想复制数组,而不是复制包含的元素。