Ruby 使用[]。替换创建阵列的副本

Ruby 使用[]。替换创建阵列的副本,ruby,Ruby,我有一个类,在这个类中,我对一个实例变量使用了Array#shiftinstance方法。我以为我复制了我的实例变量,但事实上我没有,而且shift实际上正在更改实例变量 例如,在此之前,我希望两次都能获得[“foo”、“bar”、“baz”],前提是: class Foo attr_reader :arr def initialize arr @arr = arr end def some_method foo = arr foo.shift en

我有一个类,在这个类中,我对一个实例变量使用了
Array#shift
instance方法。我以为我复制了我的实例变量,但事实上我没有,而且
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
实例调用的一个实例方法(例如
foo=[“cats”,“and”,“dogs”]。replace arr
仍然有效),而且我获得实例变量
@arr
的“副本”是有意义的

但这与:

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

或者我在上面看到的
dup
map
inject
的任何其他疯狂组合?

这是ruby中易变性的棘手概念。对于核心对象,通常会出现数组和散列。字符串也是可变的,但这可以通过脚本顶部的标志禁用。看

在这种情况下,您可以轻松调用
dup
deep\u dup
clone
,其效果与
replace
相同:

['some', 'array'].dup
['some', 'array'].deep_dup
['some', 'array'].clone
Marshal.load Marshal::dump(['some', 'array'])
就差异而言,
dup
clone
是相同的,除了一些细微的细节-请参阅

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

  a = [[1]]
  b = a.clone
  b[0][0] = 2
  a # => [[2]]
哈希也会发生同样的情况

Marshal.load Marshal::dump
是深度克隆对象的一般方法,与
deep\u dup
不同,它位于ruby核心中
Marshal::dump
返回一个字符串,以便将对象序列化到文件中


如果您想避免像这样的意外错误,请记住哪些方法有副作用,并且只在有意义时调用这些方法。方法名称末尾的解释点表示它有副作用,但其他包括取消移动、推送、压缩、删除和弹出。功能编程的很大一部分是避免副作用。您可以看到

这是ruby中易变性的棘手概念。对于核心对象,通常会出现数组和散列。字符串也是可变的,但这可以通过脚本顶部的标志禁用。看

在这种情况下,您可以轻松调用
dup
deep\u dup
clone
,其效果与
replace
相同:

['some', 'array'].dup
['some', 'array'].deep_dup
['some', 'array'].clone
Marshal.load Marshal::dump(['some', 'array'])
就差异而言,
dup
clone
是相同的,除了一些细微的细节-请参阅

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

  a = [[1]]
  b = a.clone
  b[0][0] = 2
  a # => [[2]]
哈希也会发生同样的情况

Marshal.load Marshal::dump
是深度克隆对象的一般方法,与
deep\u dup
不同,它位于ruby核心中
Marshal::dump
返回一个字符串,以便将对象序列化到文件中


如果您想避免像这样的意外错误,请记住哪些方法有副作用,并且只在有意义时调用这些方法。方法名称末尾的解释点表示它有副作用,但其他包括取消移动、推送、压缩、删除和弹出。功能编程的很大一部分是避免副作用。您可以看到首选方法是
dup

  • 需要复制阵列时,请使用
    array.dup
  • 需要复制二维数组时,请使用
    array.map(&:dup)

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

首选方法是
dup

  • 需要复制阵列时,请使用
    array.dup
  • 需要复制二维数组时,请使用
    array.map(&:dup)

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

这些方法之间的许多区别在于新数组中的对象是原始对象的副本,还是两个数组都指向相同的对象。需要注意的一点是:在ruby中赋值变量不会复制它,它只是创建一个指向该值的指针。因此,如果
arr=[1,3,4]
并指定
x=arr
,您只为原始数组创建了另一个“名称”。这些方法之间的许多区别在于,新数组中的对象是原始对象的副本,还是两个数组都指向相同的对象。需要注意的是:在ruby中分配变量不会复制它,它只会创建一个指向该值的指针。因此,如果
arr=[1,3,4]
并分配
x=arr
,则只为原始数组创建了另一个“名称”。+1。还要注意的是,关注所讨论的类如何实现这些方法是明智的。正如上面所说,“一般来说,
clone
dup
在后代类中可能有不同的语义。”我不认为数组或散列与
对象的文档有什么不同,但ActiveRecord记录可以,例如。感谢您的响应