user1726365
user1726365

Reputation: 41

Manipulating an array is reflected in all variables referring to it

If I do this

x = y = z = 1
z = 20

then I get

"#{x}----#{y}----#{z}"
# => "1----1----20" 

Now, if I do something like this:

a = b = c = []

then I get this:

"#{a}-----#{b}-----#{c}"
# => "[]-----[]-----[]"

But if I do the following:

c[0] = 'a'
c[1] = 'b'
c[2] = 'c'

I get this:

"#{a}-----#{b}-----#{c}"
# => "[\"a\", \"b\", \"c\"]-----[\"a\", \"b\", \"c\"]-----[\"a\", \"b\", \"c\"]"

In case of x, y, z, when I assigned z to 20, then x and y retained the value 1. For Arrays, even though I assigned value to c[] only, the change was reflected in a,b. What is happening with the Arrays?

Upvotes: 1

Views: 149

Answers (3)

steenslag
steenslag

Reputation: 80085

In Ruby you have methods which change it's object and methods which return new objects (the documentation of the method will mention this). Integers can not mutate: two will always be two. Playing around with the object_id may clear things up:

puts 1.object_id # 3
a = 1
puts a.object_id # 3
puts 20.object_id # 41
a = 20
puts a.object_id # 41

b = []
puts b.object_id # a number
b[1]=1 # mutating object
puts b.object_id # same number
b = b+[2] # the + method results in new object http://www.ruby-doc.org/core-1.9.3/Array.html#method-i-2B
puts b.object_id # another number

Upvotes: 0

Alex Wayne
Alex Wayne

Reputation: 187164

Think of what objects are being assigned to what variables, after all a variable is simply a handle for an object. In ruby everything is an object, and variables are just how you interact with them.

So...

a = b = 1

This sets a and b to reference the same object. If I later say:

a = 2

Then I have set a to reference a new object, which shouldn't affect b at all, which is happily still 1. The interpeter takes one simple step.

  1. set local variable a to the object 2.

But, following that logic:

a = b = []

Again, a and b reference the same object. An array this time. But now when we do this:

a[0] = 'hello'

We have a different scenario here. You aren't changing what object a references at all. You are finding the object a references and then modifying that object.

Think about what the interpreter will do. It will take the following steps when executing that line.

  1. Find the object referenced by a (which happens to be the same object referenced by b)
  2. Set the value at the 0 index of that object to the string "hello"

All this is to say that simple local variable assignment like:

a = 1

Is a somewhat different operation when there is a layer of indirection present like these:

a[0] = 'hello'
a.foo = 'bar'
a.set_value 'some val'

Or think of setting a value in an array like calling a method on that array. So the difference becomes easier to grasp if you think of a[0] = 'foo' as:

a.set_value(0, 'foo')

Something like this is actually what happens in ruby arrays. Turns out assigning a value to an array index calls the []=(index, value) method. These are all valid and equivalent.

a[0] = 'foo'
a.[]=(0, 'foo')
a.send('[]=', 0, 'foo')

I point this out only because when expressed as an invocation of a method it becomes very clear we are modifying an existing object here.

Upvotes: 1

xdazz
xdazz

Reputation: 160893

With z = 20, you are change z to reference another object,

With c[0] = 'a' , you are changing the original object which a and b is also referencing to.

Upvotes: 1

Related Questions