Как создать глубокую копию объекта в Ruby? - PullRequest
48 голосов
/ 21 ноября 2011

Я провел поиск и нашел несколько разных методов и сообщений о создании оператора глубокого копирования.

Существует ли быстрый и простой (встроенный) способ глубокого копирования объектов в Ruby?Поля не являются массивами или хешами.

Работа в Ruby 1.9.2.

Ответы [ 9 ]

65 голосов
/ 21 ноября 2011

Глубокое копирование не встроено в ванильный 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

16 голосов
/ 08 апреля 2013

Я создал собственную реализацию для глубокого клонирования объектов ruby.

Это примерно в 6-7 раз быстрее, чем подход маршала.

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

Обратите внимание, что этот проект больше не поддерживается (последний коммит в 2017 , есть сообщения проблемы )

7 голосов
/ 03 сентября 2015

В Rails есть рекурсивный метод с именем deep_dup, который возвращает глубокую копию объекта и, напротив, dup и clone, работает даже с составными объектами (массив / хэш массивов / хэшей).Это так же просто, как:

def deep_dup
  map { |it| it.deep_dup }
end
6 голосов
/ 08 апреля 2013

Существует встроенная реализация для выполнения глубоких клонов объектов ruby: ruby_deep_clone

Установите его с помощью gem:

gem install ruby_deep_clone

Пример использования:

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

Это примерно в 6-7 раз быстрее, чем подход Маршала, и событие работает с замороженными объектами.

Обратите внимание, что этот проект больше не поддерживается (последний коммит в 2017 , есть сообщения проблемы )

3 голосов
/ 31 марта 2016

Для этого вы можете использовать дублирующий камень.

Это маленький чистый рубиновый камень, который способен рекурсивно дублировать объект Он также будет дублировать ссылки на свои объекты для нового дублирования.

require 'duplicate'
duplicate('target object')

https://rubygems.org/gems/duplicate

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

2 голосов
/ 19 марта 2018

Автоматический глубокий клон не всегда то, что вы хотите.Часто вам нужно определить несколько выбранных атрибутов для глубокого клонирования.Гибкий способ сделать это - реализовать методы initialize_copy, initialize_dup и initialize_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.1015 *

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/

2 голосов
/ 27 августа 2015

Я бы посоветовал вам использовать гем ActiveSupport, который добавляет много сахара в ваше ядро ​​Ruby, а не просто метод глубокий клон .

Вы можете просмотреть документацию для получения дополнительной информации о добавленных методах.

1 голос
/ 19 января 2014

Также проверьте deep_dive. Это позволяет вам делать контролируемые глубокие копии ваших графов объектов.

https://rubygems.org/gems/deep_dive

0 голосов
/ 27 февраля 2019

Вам действительно не нужен Драгоценный камень для этого. Это не может быть намного проще, чем это, что не стоит затрат Gem!

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
...