Это распространенная ошибка новичка.
Предположим,
a = [1, 2, 3]
b = a.dup
#=> [[1, 2], [3, 4]]
b[0] = 'cat'
#=> "cat"
b #=> ["cat", 2, 3]
a #=> [1, 2, 3]
Это именно то, что вы ожидали и надеялись.Теперь рассмотрим следующее.
a = [[1, 2], [3, 4]]
b = a.dup
#=> [[1, 2], [3, 4]]
b[0] = 'cat'
b #=> ["cat", [3, 4]]
a #=> [[1, 2], [3, 4]]
Опять же, это желаемый результат.Еще один:
a = [[1,2], [3,4]]
b = a.dup
#=> [[1,2], [3,4]]
b[0][0] = 'cat'
b #=> [["cat", 2], [3, 4]]
a #=> [["cat", 2], [3, 4]]
Ааррг!Это проблема, с которой вы столкнулись.Чтобы увидеть, что здесь происходит, давайте посмотрим идентификаторы различных объектов, которые составляют a
и b
.Напомним, что каждый объект Ruby имеет уникальный Object # id .
a = [[1, 2], [3, 4]]
b = a.dup
a.map(&:object_id)
#=> [48959475855260, 48959475855240]
b.map(&:object_id)
#=> [48959475855260, 48959475855240]
b[0] = 'cat'
b #=> ["cat", [3, 4]]
a #=> [[1, 2], [3, 4]]
b.map(&:object_id)
#=> [48959476667580, 48959475855240]
Здесь мы просто заменим b[0]
, который изначально был объектом a[0]
, на другой объект ('cat'
) который, конечно, имеет другой идентификатор.Это не влияет на a
.(Ниже я приведу только три последние цифры идентификаторов. Если две одинаковые, весь идентификатор одинаков.) Теперь рассмотрим следующее.
a = [[1, 2], [3, 4]]
b = a.dup
a.map(&:object_id)
#=> [...620, ...600]
b.map(&:object_id)
#=> [...620, ...600]
b[0][0] = 'cat'
#=> "cat"
b #=> [["cat", 2], [3, 4]]
a #=> [["cat", 2], [3, 4]]
a.map(&:object_id)
#=> [...620, ...600]
b.map(&:object_id)
#=> [...620, ...600]
Мы видим, что элементы a
и b
- это те же объекты, что и до выполнения b[0][0] = 'cat'
.Однако это назначение изменило значение объекта с идентификатором ...620
, что объясняет, почему a
, а также b
, были изменены.
Чтобы избежать изменения a
, нам нужновыполните следующие действия.
a = [[1, 2], [3, 4]]
b = a.dup.map(&:dup) # same as a.dup.map { |arr| arr.dup }
#=> [[1, 2], [3, 4]]
a.map(&:object_id)
#=> [...180, ...120]
b.map(&:object_id)
#=> [...080, ...040]
Теперь элементы b
отличаются от объектов a
, поэтому любые изменения b
не влияют на a
:
b[0][0] = 'cat'
#=> "cat"
b #=> [["cat", 2], [3, 4]]
a #=> [[1, 2], [3, 4]]
Если бы у нас было
a = [[1, [2, 3]], [[4, 5], 6]]
, нам понадобилось бы dup
до трех уровней:
b = a.map { |arr0| arr0.dup.map { |arr1| arr1.dup } }
#=> [[1, [2, 3]], [[4, 5], 6]]
b[0][1][0] = 'cat'
b #=> [[1, ["cat", 3]], [[4, 5], 6]]
a #=> [[1, [2, 3]], [[4, 5], 6]]
и т. Д.