Вот более общая ситуация, которая иллюстрирует вашу проблему и способы ее устранения. Предположим, у нас были следующие вложенные массивы:
a0 = [1, 2]
a1 = [3, 4]
a = [a0, a1]
#=> [[1, 2], [3, 4]]
edges = [a]
#=> [[[1, 2], [3, 4]]]
a0
, a1
, a
и edges
имеют уникальные идентификаторы объектов:
edges.object_id #=> 1208140
a.object_id #=> 1085620
edges[0].object_id #=> 1085620
a0.object_id #=> 0977080
a[0].object_id #=> 0977080
edges[0][0].object_id #=> 0977080
a1.object_id #=> 0995980
a[1].object_id #=> 0995980
edges[0][1].object_id #=> 0995980
edges[0][1][0].object_id #=> 7
Для удобства чтения у меня естьудалены первые семь цифр каждого из идентификаторов объекта, который во всех случаях равен 4833847
. Примечание edges[0][1][0] #=> 3
и 3.object_id #=> 7
. Из соображений эффективности целые числа (и некоторые другие объекты Ruby имеют фиксированные, малые идентификаторы объектов.
Теперь создайте новый массив из edges
, используя метод Array :: new :
new_edges = Array.new(edges)
#=> [[[1, 2], [3, 4]]]
Изучите (последние шесть цифр) идентификаторы объекта:
new_edges.object_id #=> 2400460 (different than edges.object_id)
new_edges[0].object_id #=> 1085620 (same as edges[0].object_id)
new_edges[0][0].object_id #=> 0977080 (same as edges[0][0].object_id)
new_edges[0][1].object_id #=> 0995980 (same as edges[0][1].object_id)
new_edges[0][1][0].object_id #=> 7 (same as edges[0][1][0].object_id)
Видно, что new_edges
- это новый объект, но все его вложенные массивы и элементыте же объекты, что и соответствующие вложенные массивы и элементы в edges
.
Теперь давайте сделаем следующее:
edges[0][1][0] = 5
edges[0][1][0].object_id #=> 11
Тогда
edges
#=> [[[1, 2], [5, 4]]]
new_edges
#=> [[[1, 2], [5, 4]]]
new_edges
былоизменилось так же, как edges
, потому что edges[0][1]
и new_edges[0][1]
- это один и тот же объект (массив), и мы только что изменили первый элемент этого объекта.
Как избежать изменения new_edges
при edges
изменяется?
Во-первых, обратите внимание, что new_edges = Array.new(edges)
можно заменить на new_edges = edges.dup
. Как и раньше, edges
и new_edges
будут разными объектами, но все их соответствующие вложенные массивы будут одинаковыми объектами.
Мы хотим определить new_edges
, сделав глубокую копию изedges
, чтобы изменения в последнем не влияли на первое, и наоборот:
new_edges = edges.map { |a| a.map { |aa| aa.dup } }
#=> [[[1, 2], [3, 4]]]
new_edges.object_id #=> 2134620 (different than edges.object_id)
new_edges[0].object_id #=> 2134600 (different than edges[0].object_id)
new_edges[0][0].object_id #=> 2134580 (different than edges[0][0].object_id)
new_edges[0][1].object_id #=> 2134560 (different than edges[0][1].object_id)
Теперь измените вложенный элемент в edges
и наблюдайте значения edges
и new_edges
:
edges[0][1][0] = 5
edges
#=> [[[1, 2], [5, 4]]]
new_edges
#=> [[[1, 2], [3, 4]]]
Видно, что new_edges
не изменяется.
Если есть большие уровни вложенности, создание глубокой копии с использованием map
и dup
может стать утомительным и подверженным ошибкам. Более простой способ - использовать Marshal # dump и Marshal # load , которые создают глубокие копии широкого диапазона объектов Ruby, которые могут содержать несколько уровней вложенных объектов:
edges
#=> [[[1, 2], [5, 4]]]
new_edges = Marshal.load(Marshal.dump(edges))
#=> [[[1, 2], [5, 4]]]
Изменения edges
теперь оставят new_edges
без изменений.
edges[0][1][0] = 3
edges
#=> [[[1, 2], [3, 4]]]
new_edges
#=> [[[1, 2], [5, 4]]]