Reputation: 1162
I just started studying Ruby a little while ago and I was having difficulties with global versus local variable scoping.
Working on a practice problem, I found that an array defined globally was being changed by a function called on it. If I explicitly assign the array to something else, nothing changes. But if I run through and delete items one by one, this deletes them from the global array itself.
Why do delete
and pop
(which I also tested) methods have this behavior? I understood from reading that this should not be happening, that the "array" inside the functions is a reference to the values of arr
, rather than the variable arr
.
(I'm using Ruby version 2+)
def change_int x
x += 2
end
def change_arr array
array = [4, 5, 6]
end
def pop_arr array
puts array
new_array = []
while array.length > 0
new_array.push array[0]
array.delete_at 0
end
array
end
x = 5
change_int x
puts x == 5 # true
arr = [1, 2, 3]
change_arr arr
puts arr == [1, 2, 3] # true
old_arr = arr
puts pop_arr arr
puts arr == [1, 2, 3] # false
puts "arr = #{arr}" # arr = []
Upvotes: 2
Views: 1359
Reputation: 311
See also: Ruby - Parameters by reference or by value? and Is Ruby pass by reference or by value?.
The curious thing is that change_arr doesn't affect the global array, but pop_arr does, in your code.
Here's what's happening: ruby passes references to objects as parameters. So like Bartosz said, you can see that at the top of those methods, the object id matches the one you passed in; they're referencing the same object.
So, in pop_arr, when you call delete_at, you're operating on the same object that you passed in, and the changes persist after the method returns.
In change_arr, the difference is that you're assigning the internal var to a new object. When you pass in the parameter array, the internal variable references the same object you passed in. When you instantiate a new Array object and assign the internal array variable to it, the internal variable is now referencing a different object.
def change_arr array
puts "change id: #{array.object_id}"
array = [4, 5, 6]
puts "change id2: #{array.object_id}"
array
end
That's why the changes don't persist after the method ends. If you wanted the changes to persist, you'd have to say
array = change_arr(array)
Hope that helps.
Upvotes: 1
Reputation: 342
You can see by printing #object_id
before calling pop_arr
and inside pop_arr
that those arrays are the same objects. This means that arguments are passed into the function by reference in Ruby.
Here is code:
def pop_arr(array)
puts array.object_id
# Rest of the fucntion
end
arr = [1, 2, 3]
puts arr.object_id
pop_arr(arr)
All of this means that when you edit array inside the function it will have effect on the object which was passed. #delete
, #delete_at
, #pop
are operations that change the Array on which they are made.
Upvotes: 3