gpt4 book ai didi

Ruby:如何使用 dup/clone 来不改变原始实例变量?

转载 作者:数据小太阳 更新时间:2023-10-29 07:23:32 24 4
gpt4 key购买 nike

学习 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 变量实例,该实例会用 :N 代替每个 :S 符号。

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

最佳答案

这是一个常见的新手错误。

假设

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

啊!这是您遇到的问题。要了解此处发生的情况,让我们查看构成 ab 的各种对象的 ID。回想一下,每个 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]

这里我们简单地用一个不同的对象 ('cat') 替换了最初是对象 a[0] ) 当然有不同的 id。这不会影响 a。 (在下文中,我将仅给出 ID 的最后三位数字。如果两个相同,则整个 ID 相同。)现在考虑以下内容。

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]

我们看到 ab 的元素与执行之前的对象相同 b[0][0] = 'cat'。但是,该赋值更改了 id 为 ...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]]

我们需要复制到三个级别:

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

等等。

关于Ruby:如何使用 dup/clone 来不改变原始实例变量?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/54354090/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com