使用self.dup,但是没有通过rspec测试来修改原始数组

时间:2016-08-09 03:13:35

标签: arrays ruby

我正在创建一种转置方形二维数组的方法。我的方法通过了每个测试,除了“不修改原始数组”之外。我只是在处理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)>'

2 个答案:

答案 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 }

arrorig_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)

通过此修改,您的测试应该通过。