Reputation: 53
Why is my array globally manipulated, when I run the below ruby code? And how can I get arrays to be manipulated only within the function's scope?
a = [[1,0],[1,1]]
def hasRowsWithOnlyOnes(array)
array.map { |row|
return true if row.keep_if{|i| i != 1 } == []
}
false;
end
puts a.to_s
puts hasRowsWithOnlyOnes(a)
puts a.to_s
$ ruby test.rb
output:
[[1, 0], [1, 1]]
true
[[0], []]
I can't get it to work. I've even tried .select{true}
and assign it to a new name. How does the scope work in Ruby for Arrays?
Just for reference, $ ruby -v
:
ruby 2.2.1p85 (2015-02-26 revision 49769) [x86_64-linux]
Upvotes: 3
Views: 197
Reputation: 108129
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 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.
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.
Upvotes: 7
Reputation: 1787
Ruby is pass by value
Via sitepoint
Ruby has variables defined within different scopes, which you probably know already. I found that most tutorials describe them briefly (the variable types), but they fail to mention precisely what their scope is.
Here are the details:
Class variable (@@a_variable
): Available from the class definition and any sub-classes. Not available from anywhere outside.
Instance variable (@a_variable
): Available only within a specific object, across all methods in a class instance. Not available directly from class definitions.
Global variable ($a_variable
): Available everywhere within your Ruby script.
Local variable (a_variable
): It depends on the scope. You’ll be working with these most and thus encounter the most problems, because their scope depends on various things.
Upvotes: 0