我正在创建一种转置方形二维数组的方法。我的方法通过了每个测试,除了“不修改原始数组”之外。我只是在处理duped数组,所以我对测试失败的原因感到困惑。
代码:
class Array
def my_transpose
orig_arr = self.dup; array = []
orig_arr[0].length.times do
temp_arr = []
orig_arr.each { |arr| temp_arr << arr.shift }
array << temp_arr
end
array
end
end
RSpec的:
describe Array do
describe "#my_transpose" do
let(:arr) { [
[1, 2, 3],
[4, 5, 6],
[7, 8, 9]
] }
let(:small_arr) { [
[1, 2],
[3, 4]
] }
it "transposes a small matrix" do
expect(small_arr.my_transpose).to eq([
[1, 3],
[2, 4]
])
end
it "transposes a larger matrix" do
expect(arr.my_transpose).to eq([
[1, 4, 7],
[2, 5, 8],
[3, 6, 9]
])
end
it "should not modify the original array" do
small_arr.my_transpose
expect(small_arr).to eq([
[1, 2],
[3, 4]
])
end
it "should not call the built-in #transpose method" do
expect(arr).not_to receive(:transpose)
arr.my_transpose
end
end
end
输出:
7) Array#my_transpose should not modify the original array
Failure/Error: expect(small_arr).to eq([
expected: [[1, 2], [3, 4]]
got: [[], []]
(compared using ==)
# ./spec/00_array_extensions_spec.rb:123:in `block (3 levels) in <top (required)>'
答案 0 :(得分:2)
当你在一个数组上调用dup
时,它只复制数组本身;数组的内容也不重复。所以,例如:
a = [[1,2],[3,4]]
b = a.dup
a.object_id == b.object_id # => false
a[0].object_id == b[0].object_id # => true
因此,对a
本身的修改不会反映在b
中(反之亦然),而是{{>>元素中的修改a
1}}反映在b
中,因为这些元素是相同的对象。
在这种情况下,问题出现在这里:
orig_arr.each { |arr| temp_arr << arr.shift }
arr
是orig_arr
的元素,但也是 self
的元素。如果您从orig_arr
执行删除之类的操作,则不会将其从self
中删除,但如果您更改它,则无论您如何访问它都会更改它事实证明,Array#shift
是一种破坏性的行动。
您可以对代码进行最小的更改,使其按预期工作,使用each_with_index
,然后将索引用于arr
,而不是调用arr.shift
,所以:
orig_arr.each_with_index { |arr,i| temp_arr << arr[i] }
事实上,一旦你这样做,你根本就没有做任何破坏性的操作而你不需要orig_arr
,你可以只使用self
。
答案 1 :(得分:1)
原始数组未被修改,但中的数组,因为dup
是一个浅层克隆。
xs = [[1,2],[3,4]]
ids = xs.map(&:object_id)
xs.my_transpose
ids == xs.map(&:object_id) #=> true
由于shift
是一个变异操作(在嵌套数组元素上执行),因此你需要dup
数组中的元素,例如。
orig_arr = dup.map(&:dup)
通过此修改,您的测试应该通过。