Как создать глубокую копию объекта в Ruby?
Я искал несколько разных методов и сообщений о создании оператора глубокой копии.
Есть ли быстрый и простой (встроенный) способ для глубоких копий объектов в Ruby? Поля не являются массивами или хешами.
Работа в Ruby 1.9.2.
Ответы
Ответ 1
Глубокая копия не встроена в ванильный рубин, но вы можете взломать ее, маршируя и раскручивая объект:
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
Ответ 2
Я создал нативную реализацию для выполнения глубоких клонов объектов ruby.
Это примерно в 6-7 раз быстрее, чем подход маршала.
https://github.com/balmma/ruby-deepclone
Обратите внимание, что этот проект больше не поддерживается (последний коммит в 2017 году, есть сообщения о проблемах)
Ответ 3
Существует нативная реализация для выполнения глубоких клонов объектов ruby: ruby_deep_clone
Установите его с помощью gem:
gem install ruby_deep_clone
Пример использования:
require "deep_clone"
object = SomeComplexClass.new()
cloned_object = DeepClone.clone(object)
Это примерно в 6-7 раз быстрее, чем подход маршала, и событие работает с замороженными объектами.
Обратите внимание, что этот проект больше не поддерживается (последний коммит в 2017 году, есть сообщения о проблемах)
Ответ 4
Rails имеет рекурсивный метод с именем deep_dup
, который вернет глубокую копию объекта и, напротив, dup
и clone
, работает даже на составных объектах (массив/хэш массивов/хэшей).
Это легко:
def deep_dup
map { |it| it.deep_dup }
end
Ответ 5
Вы можете использовать для этого дубликат драгоценности.
Это небольшой чистый рубиновый камень, способный рекурсивно дублировать объект
Он также дублирует ссылки на объекты на новое дублирование.
require 'duplicate'
duplicate('target object')
https://rubygems.org/gems/duplicate
https://github.com/adamluzsi/duplicate.rb
Ответ 6
Я бы посоветовал использовать массив ActiveSupport, который добавляет много сахара в ваше родное ядро Ruby, а не только метод глубокого клонирования.
Более подробную информацию о добавленных методах можно найти в документации.
Ответ 7
Автоматический глубокий клон - это не всегда то, что вы хотите. Часто вам нужно определить несколько атрибутов для глубокого клонирования. Гибкий способ сделать это - реализовать методы 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.
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/
Ответ 8
Также проверьте deep_dive. Это позволяет выполнять контролируемые глубинные копии графиков объектов.
https://rubygems.org/gems/deep_dive
Ответ 9
Вам действительно не нужен драгоценный камень для этого. Это не может быть намного проще, чем это, что не стоит затрат на драгоценный камень!
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