Почему в Ruby нет метода глубокой копии?

Я работаю над решением технических чертежей (svg/ruby). Я хочу манипулировать прямоугольниками и иметь метод add! в этом классе:

class Rect
  def add!(delta)
    @x1+=delta 
    ... # and so on
    self
  end
end

Мне также нужен метод add, возвращающий Rect, но не манипулирующий self:

def add(delta)
  r=self.dup/clone/"copy" # <-- not realy the 3 and no quotes, just in text here
  r.add! delta
end

dup и clone не делают ничего, кроме:

def copy; Marshal.load(Marshal.dump(self)); end

делает.

Почему такая базовая функциональность не существует в простом Ruby? Пожалуйста, просто не говорите мне, что я мог бы перевернуть add и add!, позволяя add выполнить задание и add! вызвать его.

Ответ 1

Я не уверен, почему в Ruby нет метода глубоких копий, но я попытаюсь сделать обоснованное предположение на основе информации, которую я смог найти (см. ссылки и цитаты ниже строки).

Судя по этой информации, я мог только предположить, что причина, по которой Ruby не имеет метода глубокой копии, состоит в том, что она очень редко необходима, и в тех немногих случаях, когда это действительно необходимо, существуют и другие, относительно простые способы достижения та же задача:

Как вы уже знаете, использование Marshal.dump и Marshal.load в настоящее время является рекомендуемым способом для этого. Это также подход, рекомендованный программированием Ruby (см. Выдержки ниже).

В качестве альтернативы в этих драгоценных камнях есть как минимум 3 доступных реализации: deep_cloneable, deep_clone и ruby_deep_clone; первый из них является самым популярным.


Связанная информация

Здесь обсуждение в comp.lang.ruby, которое может пролить свет на это. Здесь другой ответ здесь с некоторыми связанными обсуждениями, но все это возвращается к использованию Marshal.

Не было никаких упоминаний о глубоком копировании в Programming Ruby, но было несколько упоминаний в Язык программирования Ruby. Вот несколько связанных выдержек:

[...]

Другим использованием для Marshal.dump и Marshal.load является создание глубоких копий объектов:

def deepcopy(o)
  Marshal.load(Marshal.dump(o))
end

[...]

... двоичный формат, используемый Marshal.dump и Marshal.load, равен версии, зависящие от версии, и более новые версии Ruby не гарантируются способный читать маршированные объекты, написанные более старыми версиями Ruby.

[...]

Обратите внимание, что файлы и потоки ввода-вывода, а также метод и привязка объекты, слишком динамичны для сортировки; не было бы надежного способ восстановить их состояние.

[...]

Вместо того чтобы сделать защитную глубокую копию массива, просто позвоните to_enum на нем и передать результирующий счетчик вместо массива сам. По сути, вы создаете перечислимый, но неизменяемый прокси-сервер объект для вашего массива.

Ответ 2

Забудьте о сортировке. Драгоценный камень deep_dive решит ваши проблемы.

https://rubygems.org/gems/deep_dive

Ответ 3

Почему вы не можете использовать что-то вроде этого:

new_item = Item.new(old_item.attributes)
new_item.save!

Это скопирует все атрибуты из существующего элемента в новый, без проблем. Если у вас есть другие объекты, вы можете просто скопировать их по отдельности.

Я думаю, что это самый быстрый способ скопировать объект