It's not a scope problem, it's an argument passing problem
All variables in Ruby are references to objects.
When you pass an object to a method, a copy of that object's reference is made and passed to the object.
That means that the variable array
in your method and the top-level variable a
refer to the exact same Array. Any changes made to array
will be also visible as changes to a
, since both variables refer to the same object.
Your method does modify the array by calling Array#keep_if. The keep_if
method modifies the array in-place.
The fix
The best fix for this is to make it so that your method does not modify the array that was passed in. This can be done pretty neatly using the Enumerable#any? and Enumerable#all? methods:
def has_a_row_with_only_ones(array)
array.any? do |row|
row.all? { |e| e == 1 }
end
end
This code says that the method returns true if, for any row, every element in that row is 1. These methods do not modify the array. More important, they communicate the method's intent clearly.
The poor workaround
If you want the method to act as through a copy of the array were passed to it, so that the array can be modified without that modification being visible outside the method, then you can make a deep copy of the array. As shown in this answer, you can define this method to make a deep copy:
def deep_copy(o)
Marshal.load(Marshal.dump(o))
end
Then, at the top of the method, make the deep copy:
def has_a_row_with_only_ones(array)
array = deep_copy(array)
# code that modifies array
end
This should be avoided because it's slow.