Ruby: Как использовать dup / clone, чтобы не изменять исходную переменную экземпляра? - PullRequest
0 голосов
/ 24 января 2019

Изучая Ruby, я создаю проект Battleship, и у меня есть следующий код в качестве метода экземпляра для класса "Board", который я создаю.

def hidden_ships_grid
    hidden_s_grid = @grid.dup 
    hidden_s_grid.each_with_index do |sub_array, i|
        sub_array.each_with_index do |el, j|
            # position = [i, j]
            hidden_s_grid[i][j] = :N if el == :S 
       end
    end
end

По сути, этот метод создаст еще один экземпляр переменной @grid, который заменит каждый символ: S вместо: N.

RSPEC предъявляет два требования: 1) «должен возвращать двумерный массив, представляющий сетку, где каждое: S заменяется на: N» и 2) «не должно изменять исходную @grid».

Моя проблема в том, что приведенный выше код удовлетворяет первому требованию, но нарушает второе. Может кто-нибудь объяснить мне, что вызывает мутацию исходного файла @grid? Я просмотрел код 15 раз и не вижу, где переписать или переназначить исходную переменную @grid.

«Правильное» решение, предоставленное нам, использует «.map», что хорошо, но я хочу понять, почему это решение не работает и в конечном итоге изменяет исходную переменную @grid.

  1) Board PART 2 #hidden_ships_grid should not mutate the original @grid
     Failure/Error: expect(board.instance_variable_get(:@grid)).to eq([[:S, :N],[:X, :S]])

       expected: [[:S, :N], [:X, :S]]
            got: [[:N, :N], [:X, :N]]

       (compared using ==)

       Diff:
       @@ -1,2 +1,2 @@
       -[[:S, :N], [:X, :S]]
       +[[:N, :N], [:X, :N]]

Заранее спасибо!

Ответы [ 3 ]

0 голосов
/ 24 января 2019

Это распространенная ошибка новичка.

Предположим,

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]]

и т. Д.

0 голосов
/ 25 января 2019

Оба параметра dup и clone предназначены для создания дублирующих записей.

Вы можете ссылаться на эту ссылку для dup Dup Api Documents

Вы можетесм. эту ссылку для clone клон Api Documents

0 голосов
/ 24 января 2019

dup и clone мелкие. Вы копируете содержимое массива сетки, которое является ссылкой на внутренний массив. Эти массивы не копируются, а ссылаются. Вот почему оригинальная сетка меняется позже.

Вам нужно отобразить сетку и дублировать внутренние массивы

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...