Как делать глубокие копии на Ruby

Часто бывает необходимо сделать копию значения в Ruby. Хотя это может показаться простым, и это касается простых объектов, как только вам нужно сделать копию структуры данных с несколькими массивами или хешами для одного и того же объекта, вы быстро обнаружите, что есть много подводных камней.

Объекты и ссылки

Чтобы понять, что происходит, давайте посмотрим на простой код. Во-первых, оператор присваивания, использующий тип POD (простые старые данные) в Ruby.

a = 1
b = a
a + = 1
помещает b

Здесь оператор присваивания делает копию значения a и присвоив его b с помощью оператора присваивания. Любые изменения в a не будут отражены в b . Но как насчет чего-то более сложного? Учтите это.

a = [1,2]
b = a
a помещает b.inspect

Перед запуском указанной выше программы попытайтесь угадать, каким будет результат и почему. Это не то же самое, что и в предыдущем примере, изменения, внесенные в a , отражаются в b , но почему? Это связано с тем, что объект Array не является типом POD. Оператор присваивания не копирует значение, он просто копирует ссылку в объект Array. Переменные a и b теперь являются ссылками на один и тот же объект Array, любые изменения в любой переменной будут видны в другой.

И теперь вы можете понять, почему копирование нетривиальных объектов со ссылками на другие объекты может быть сложной задачей. Если вы просто делаете копию объекта, вы просто копируете ссылки на более глубокие объекты, поэтому ваша копия называется «неглубокой копией».

Что предоставляет Ruby: dup и clone

Ruby действительно предоставляет два метода для создания копий объектов, в том числе тот, который может быть создан для создания глубоких копий. Метод Object # dup создает неглубокую копию объекта. Для этого метод dup вызовет метод initialize_copy этого класса. Что именно это делает, зависит от класса. В некоторых классах, таких как Array, он инициализирует новый массив с теми же членами, что и исходный массив. Однако это не полная копия. Обратите внимание на следующее.

a = [1,2]
b = a.dup
a помещает b.inspect
a = [[1,2]]
b = a.dup
a [0] помещает b .inspect

Что здесь произошло? Метод Array # initialize_copy действительно создает копию массива, но эта копия сама по себе является неглубокой копией. Если в вашем массиве есть другие типы, не относящиеся к POD, использование dup будет только частичной глубокой копией. Он будет только глубже первого массива, любые более глубокие массивы, хэши или другие объекты будут копироваться только неглубоко.

Есть еще один метод, о котором стоит упомянуть, клон . Метод clone делает то же самое, что и dup , с одним важным отличием: ожидается, что объекты заменят этот метод тем, который может делать глубокие копии.

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

Уловка: маршалинг

” Маршаллинг объекта – это еще один способ сказать “сериализацию” объекта. Другими словами, превратите этот объект в поток символов, который можно записать в файл, который вы можете «демаршалировать» или «десериализовать» позже, чтобы получить тот же объект. Это можно использовать для получения полной копии любого объекта.

a = [[1,2]]
b = Marshal.load (Marshal.dump (a))
a [0] помещает b.inspect

Что произошло здесь? Marshal.dump создает «дамп» вложенного массива, хранящегося в a . Этот дамп представляет собой двоичную символьную строку, предназначенную для хранения в файле. В нем находится полное содержимое массива, полная глубокая копия. Затем Marshal.load делает обратное. Он анализирует этот двоичный массив символов и создает совершенно новый массив с полностью новыми элементами массива.

Но это уловка. Это неэффективно, не будет работать со всеми объектами (что произойдет, если вы попытаетесь таким образом клонировать сетевое соединение?) И, вероятно, не очень быстро. Однако это самый простой способ сделать глубокие копии без использования пользовательских методов initialize_copy или clone . То же самое можно сделать с помощью таких методов, как to_yaml или to_xml , если у вас есть загруженные библиотеки для их поддержки.

Оцените статью
recture.ru
Добавить комментарий