I'm creating a method to transpose square 2-d arrays. My method passes every test, except the "does not modify original array" one. I'm only working on the duped array, so I'm confused on why the test is failing.
Code:
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
Output:
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)>'
When you call dup
on an array, it only duplicates the array itself; the array's contents are not also duplicated. So, for example:
a = [[1,2],[3,4]]
b = a.dup
a.object_id == b.object_id # => false
a[0].object_id == b[0].object_id # => true
Thus, modifications to a
itself are not reflected in b
(and vice versa), but modifications in the elements of a
are reflected in b
, because those elements are the same objects.
That being the case, the problem crops up here:
orig_arr.each { |arr| temp_arr << arr.shift }
arr
is an element of orig_arr
, but it is also an element of self
. If you did something like remove it from orig_arr
, you would not also remove it from self
, but if you change it, it's changed, no matter how you are accessing it, and as it turns out, Array#shift
is a destructive operation.
Probably the smallest change you could make to your code to make it work as you expect would be to use each_with_index
, and then use the index into arr
, rather than calling arr.shift
, so:
orig_arr.each_with_index { |arr,i| temp_arr << arr[i] }
In fact, though, once you're doing that, you're not doing any destructive operations at all and you don't need orig_arr
, you can just use self
.
The original array isn't being modified, but the arrays within it are, as dup
is a shallow clone.
xs = [[1,2],[3,4]]
ids = xs.map(&:object_id)
xs.my_transpose
ids == xs.map(&:object_id) #=> true
Since shift
is a mutating operation (being performed on the nested array elements), you need to dup
the elements within the array as well, eg
orig_arr = dup.map(&:dup)
With this modification, your test should pass.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.