如何在Ruby中创建对象的深层副本?

时间:2011-11-21 02:04:22

标签: ruby-on-rails ruby object copy deep-copy

我做了一些搜索,发现了一些关于创建深拷贝操作符的不同方法和帖子。

是否有一种快速简便的(内置)方式在Ruby中深度复制对象?字段不是数组或散列。

使用Ruby 1.9.2。

9 个答案:

答案 0 :(得分:62)

深层拷贝并不构建在vanilla Ruby中,但您可以通过编组和解组对象来破解它:

Marshal.load(Marshal.dump(@object))

虽然这并不完美,但并不适用于所有物体。一种更强大的方法:

class Object
  def deep_clone
    return @deep_cloning_obj if @deep_cloning
    @deep_cloning_obj = clone
    @deep_cloning_obj.instance_variables.each do |var|
      val = @deep_cloning_obj.instance_variable_get(var)
      begin
        @deep_cloning = true
        val = val.deep_clone
      rescue TypeError
        next
      ensure
        @deep_cloning = false
      end
      @deep_cloning_obj.instance_variable_set(var, val)
    end
    deep_cloning_obj = @deep_cloning_obj
    @deep_cloning_obj = nil
    deep_cloning_obj
  end
end

来源:

http://blade.nagaokaut.ac.jp/cgi-bin/scat.rb/ruby/ruby-list/43424

答案 1 :(得分:16)

我已经创建了一个本机实现来执行ruby对象的深层克隆。

它比Marshal方法快6到7倍。

https://github.com/balmma/ruby-deepclone

请注意,此项目不再维护(2017中的最后一次提交,已报告issues

答案 2 :(得分:7)

Rails有一个名为deep_dup的递归方法,它将返回一个对象的深层副本,与dupclone相反,它甚至可以在复合对象上工作(数组/哈希)数组/哈希)。 它很简单:

def deep_dup
  map { |it| it.deep_dup }
end

答案 3 :(得分:6)

有一个本机实现来执行ruby对象的深层克隆:ruby_deep_clone

使用gem安装它:

gem install ruby_deep_clone

使用示例:

require "deep_clone"
object = SomeComplexClass.new()
cloned_object = DeepClone.clone(object)

它比Marshal方法快6到7倍,事件与冻结物体一起使用。

请注意,此项目不再维护(2017中的最后一次提交,已报告issues

答案 4 :(得分:3)

你可以使用重复的宝石。

这是一个小的纯红宝石宝石,能够递归复制对象 它也会将它的对象引用复制到新的复制中。

require 'duplicate'
duplicate('target object')

https://rubygems.org/gems/duplicate

https://github.com/adamluzsi/duplicate.rb

答案 5 :(得分:2)

我建议您使用ActiveSupport gem,它会为您的原生Ruby核心添加大量糖,而不仅仅是deep clone方法。

您可以查看documentation以获取有关已添加方法的更多信息。

答案 6 :(得分:2)

自动深度克隆并不总是您想要的。通常,您需要定义选定的几个属性以进行深度克隆。一种灵活的方法是实施initialize_copyinitialize_dupinitialize_clone方法。

如果您有课程:

class Foo
  attr_accessor :a, :b
end

并且您只希望深度克隆:b,而是覆盖initialize_*方法:

class Foo
  attr_accessor :a, :b

  def initialize_dup(source)
    @b = @b.dup
    super
  end
end

当然,如果您希望@b也深深克隆一些自己的属性,那么您在b的课程中也会这样做。

Rails这样做(见https://github.com/rails/rails/blob/0951306ca5edbaec10edf3440d5ba11062a4f2e5/activemodel/lib/active_model/errors.rb#L78

有关更完整的解释,我在此帖https://aaronlasseigne.com/2014/07/20/know-ruby-clone-and-dup/

中了解到了这一点

答案 7 :(得分:1)

同时查看deep_dive。这允许您对对象图进行受控深度复制。

https://rubygems.org/gems/deep_dive

答案 8 :(得分:0)

您真的不需要宝石。这比这简单得多,这不值得拥有宝石的开销!

def deep_clone(obj)
  obj.clone.tap do |new_obj|
    new_obj.each do |key, val|
      new_obj[key] = deep_clone(val) if val.is_a?(Hash)
    end
  end
end