使用[] .replace制作数组

时间:2017-01-15 02:06:02

标签: ruby

我有一个类,我在实例变量上使用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"]

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

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

然后我发现another answer字面上只有一行解决了我的问题:

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的实例上调用的实例方法恰好是一个空数组(例如foo = ["cats", "and", "dogs"].replace arr仍然可以工作)并且它是有意义的我得到了实例变量@arr的“副本”。

但这有什么不同于:

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

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

2 个答案:

答案 0 :(得分:2)

这是红宝石中可变性的棘手概念。就核心对象而言,这通常会产生数组和散列。字符串也是可变的,但可以通过脚本顶部的标志禁用。请参阅What does the comment "frozen_string_literal: true" do?

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

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

就差异而言,dupclone是相同的,除了一些细微差别的细节 - 请参阅What's the difference between Ruby's dup and clone methods?

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

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

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

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

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

答案 1 :(得分:2)

首选方法是dup

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

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