A little bit confused about array+array addition (bug or expected behavior?)

a = [1,2]

=> [1, 2]

a

=> [1, 2, 3, 4]
irb(main):082:0> a
=> [1, 2, 3, 4]

So far so good...

irb(main):082:0> def add(x)
irb(main):084:1> x += [5,6]
irb(main):085:1> end

irb(main):087:0> add(a)
=> [1, 2, 3, 4, 5, 6]
irb(main):088:0> a
=> [1, 2, 3, 4] ===>>> uh? am I not passing stuff by reference?
where is 5, 6?

irb(main):089:0> def add2(x)
irb(main):089:0> x << [5,6]
irb(main):089:0> end

irb(main):090:0> add2(a)
=> [1, 2, 3, 4, [5, 6]]
irb(main):091:0> a
=> [1, 2, 3, 4, [5, 6]] ==> seems logical, but then .... why +=
behaves differently?

ruby -v

ruby 1.8.2 (2004-12-25) [i686-linux]

gga wrote:

irb(main):082:0> def add(x)
irb(main):084:1> x += [5,6]
irb(main):085:1> end

irb(main):087:0> add(a)
=> [1, 2, 3, 4, 5, 6]
irb(main):088:0> a
=> [1, 2, 3, 4] ===>>> uh? am I not passing stuff by reference?
where is 5, 6?

You are not passing variables by reference, you are passing objects by reference.

Ruby does not have a built-in way of doing variable by reference as variables are usually not first-class objects. (They are names for objects.)

You probably want to do x.replace(x + [5, 6]) or x.push(5, 6) which modify the object itself.

This has come up frequently in the past -- if you are willing to do a bit of research on google groups you will find implementations of first-class variable objects, using lambdas for modifying variables in other scopes and more.

gga wrote:

irb(main):082:0> def add(x)
irb(main):084:1> x += [5,6]
irb(main):085:1> end

irb(main):087:0> add(a)
=> [1, 2, 3, 4, 5, 6]
irb(main):088:0> a
=> [1, 2, 3, 4] ===>>> uh? am I not passing stuff by

reference?

where is 5, 6?

In the method body you are reassing to the local variable x.
.. x += [...]
is translated to:
.. x = x + [...]
and #+ as we all know returns a copy of the object.

You can see this with:

.. def add(x)
.. puts "Before #{x.object_id}"
.. x += [5,6]
.. puts "After #{x.object_id}"
.. end

So you ARE passing by ref, but then changing the ref to point to
anotehr object.

irb(main):089:0> def add2(x)
irb(main):089:0> x << [5,6]
irb(main):089:0> end

irb(main):090:0> add2(a)
=> [1, 2, 3, 4, [5, 6]]
irb(main):091:0> a
=> [1, 2, 3, 4, [5, 6]] ==> seems logical, but then .... why +=
behaves differently?

The METHOD #<< modifies the object in place. The SYNTACTIC SUGAR += is
changing a objeced referred to.

HTH,
Assaph

Yes, that's what I had figured. I had not realized the existance of
the replace method. That's exactly what I was looking for.

"Assaph Mehr" <assaph@gmail.com> schrieb im Newsbeitrag
news:1108596235.249240.166770@z14g2000cwz.googlegroups.com...

gga wrote:
>
> irb(main):082:0> def add(x)
> irb(main):084:1> x += [5,6]
> irb(main):085:1> end
>
> irb(main):087:0> add(a)
> => [1, 2, 3, 4, 5, 6]
> irb(main):088:0> a
> => [1, 2, 3, 4] ===>>> uh? am I not passing stuff by
reference?
> where is 5, 6?

In the method body you are reassing to the local variable x.
. x += [...]
is translated to:
. x = x + [...]
and #+ as we all know returns a copy of the object.

You can see this with:

. def add(x)
. puts "Before #{x.object_id}"
. x += [5,6]
. puts "After #{x.object_id}"
. end

So you ARE passing by ref, but then changing the ref to point to
anotehr object.

> irb(main):089:0> def add2(x)
> irb(main):089:0> x << [5,6]
> irb(main):089:0> end
>
> irb(main):090:0> add2(a)
> => [1, 2, 3, 4, [5, 6]]
> irb(main):091:0> a
> => [1, 2, 3, 4, [5, 6]] ==> seems logical, but then .... why +=
> behaves differently?

The METHOD #<< modifies the object in place. The SYNTACTIC SUGAR += is
changing a objeced referred to.

Alternative is #concat

a=[1,2]

=> [1, 2]

def add3(a) a.concat [3,4] end

=> nil

add3 a

=> [1, 2, 3, 4]

a

=> [1, 2, 3, 4]

Regards

    robert

gga wrote:

irb(main):082:0> def add(x)
irb(main):084:1> x += [5,6]

This creates a new array and assigns it to the local variable x.
The original array passed into this method won't be modified.
But the result of the statement is the new array, which will
be returned.

irb(main):085:1> end

irb(main):087:0> add(a)
=> [1, 2, 3, 4, 5, 6]
irb(main):088:0> a
=> [1, 2, 3, 4] ===>>> uh? am I not passing stuff by reference?
where is 5, 6?

irb(main):089:0> def add2(x)
irb(main):089:0> x << [5,6]

<< appends to the existing array

···

irb(main):089:0> end

In Message-Id: <1108621386.785917.174190@f14g2000cwb.googlegroups.com>
"gga" <GGarramuno@aol.com> writes:

Yes, that's what I had figured. I had not realized the existance of
the replace method. That's exactly what I was looking for.

But what you need in this particular case is Array#concat.

    irb(main):001:0> a=[1,2,3]
    => [1, 2, 3]
    irb(main):002:0> a.concat([4,5])
    => [1, 2, 3, 4, 5]
    irb(main):003:0> a
    => [1, 2, 3, 4, 5]

···

--
kjana@dm4lab.to February 17, 2005
Time and tide wait for no man.