Why do arrays work this way?

N

Nigel Wilkinson

Hi folks

I'm using ruby 1.8.2 as comes with Mandriva 2005LE and get the following
behaviour with arrays which doesn't seem logical to me.

In irb I do

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

straight forward enough

irb(main):059:0> b=a
=> [1, 2, 3, 4, 5]
irb(main):060:0> c=a
=> [1, 2, 3, 4, 5]

I've set 2 more variables to equal 'a'

irb(main):061:0> b[3] = 'w'
=> "w"

I believe this changes index 3 in'b' to now be equal to 'w'

do
irb(main):062:0> b
=> [1, 2, 3, "w", 5]

that's exactly as I expected, BUT

irb(main):063:0> a
=> [1, 2, 3, "w", 5]
irb(main):064:0> c
=> [1, 2, 3, "w", 5]

so what's the logic behind 'a' & 'c' changing when I change 'b'.

Is this a bug? and if not what's the reasoning behind this behaviour please.



Best Rgds from confused of Heathfield
Nigel
__________________________________________________________________________
Disclaimer
Any opinions expressed in this email are not necessarily those of my wife
 
J

Joel VanderWerf

Nigel said:
Hi folks

I'm using ruby 1.8.2 as comes with Mandriva 2005LE and get the following
behaviour with arrays which doesn't seem logical to me.

In irb I do

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

straight forward enough

irb(main):059:0> b=a
=> [1, 2, 3, 4, 5]
irb(main):060:0> c=a
=> [1, 2, 3, 4, 5]

I've set 2 more variables to equal 'a'

irb(main):061:0> b[3] = 'w'
=> "w"

I believe this changes index 3 in'b' to now be equal to 'w'

do
irb(main):062:0> b
=> [1, 2, 3, "w", 5]

that's exactly as I expected, BUT

irb(main):063:0> a
=> [1, 2, 3, "w", 5]
irb(main):064:0> c
=> [1, 2, 3, "w", 5]

so what's the logic behind 'a' & 'c' changing when I change 'b'.

It's the same array in each case. The 3 variables each refer to it.
 
J

James Edward Gray II

Hi folks

I'm using ruby 1.8.2 as comes with Mandriva 2005LE and get the
following behaviour with arrays which doesn't seem logical to me.

[snip examples]
so what's the logic behind 'a' & 'c' changing when I change 'b'.

Is this a bug? and if not what's the reasoning behind this
behaviour please.

In Ruby, everything is an object, but variables are just references
to those objects. Your `b=a` sets b to reference the same object as
a. Thus, when you make a change to one, they all change. Now, if
you used `b=a.dup` b would be set to reference a copy of a, and the
changes would not filter over.

Try playing around in irb as you have been doing, but calling
object_id() on the arrays to seem which objects are the same.

Hope that helps.

James Edward Gray II
 
K

Kent Sibilev

When you make an assignment b =3D a, you assign the variable 'b' a
reference to the same array variable 'a' refers to. If you want to
have a copy of array a use dup method like

b =3D a.dup

Kent.
 
D

Devin Mullins

As in many modern languages (Java, most popularly, but I'd imagine
Python, too), everything's a pointer, so to speak. If you want a copy,
you should say b=a.dup, instead. Note, however, that not even that's
perfect.

While this works:

irb(main):001:0> a=[1,2,3,4,5]
=> [1, 2, 3, 4, 5]
irb(main):002:0> b=a.dup
=> [1, 2, 3, 4, 5]
irb(main):003:0> b[2]=7
=> 7
irb(main):004:0> a
=> [1, 2, 3, 4, 5]
irb(main):005:0> b
=> [1, 2, 7, 4, 5]
irb(main):006:0>

There is still a pitfall if the Objects that your Array contains are
mutable (changeable):

irb(main):006:0> a=["hello","dog","cat"]
=> ["hello", "dog", "cat"]
irb(main):007:0> b=a.dup
=> ["hello", "dog", "cat"]
irb(main):008:0> b[2]="feline"
=> "feline"
irb(main):009:0> b[1].reverse!
=> "god"
irb(main):010:0> a
=> ["hello", "god", "cat"]
irb(main):011:0> b
=> ["hello", "god", "feline"]
irb(main):012:0>

Note that b[2]=... makes the new separate b Array point to a brand new
String object, while b[1].reverse! modifies the existing String object
-- the one that both a and b both point to. This is because Array#dup
(and in general Object#dup) is a shallow copy. In other words,
Object#dup is not "recursive" on the Objects inside.

Hope that helps, and hope it doesn't kill your buzz. :)

Devin
 
E

Erik Veenstra

a=[1,2,3,4,5]

This creates a specific object of class Array. "a" is a pointer
to the array-object. Just to be sure, I didn't say: "a" is the
array-object...

Now "b" and "c" point to the same object as "a".
so what's the logic behind 'a' & 'c' changing when I change
'b'.

I think you can now answer it yourself...
Is this a bug? and if not what's the reasoning behind this
behaviour please.

There's only one type for variables in Ruby: pointer. Pointer
to nothing (=nil) or pointer to an object. There's no such
thing as storing an object in a variable.

Have you read "Programming Ruby, The Pragmatic Programmer's
Guide" [1]?

Extra info: In Java, there's a difference between "int" and
"Integer". Variables of type "int" store direct values, whereas
variables of type "Integer" only store pointers, like in Ruby.
Pointers in Ruby are typeless and pointers in Java are of a
certain type.

gegroet,
Erik V. - http://www.erikveen.dds.nl/

[1] http://www.rubycentral.com/book/index.html
 
E

Erik Veenstra

As in many modern languages (Java, most popularly, but I'd
imagine Python, too), everything's a pointer, so to speak.

Not everything in Java is a pointer. Compare "int" with
"Integer": a huge difference.

Sorry, I didn't realize that this is the Ruby news groups...

gegroet,
Erik V. - http://www.erikveen.dds.nl/
 
F

Florian Groß

Nigel said:
irb(main):058:0> a=[1,2,3,4,5]; b = a; b[3] = 'w'; a
=> [1, 2, 3, "w", 5]

so what's the logic behind 'a' & 'c' changing when I change 'b'.

Is this a bug? and if not what's the reasoning behind this behaviour
please.

In Ruby variables are just names for objects. If you do
my_array = [1, 2, 3]
you name an array with the elements 1, 2 and 3 as my_array.

If you then do
my_numbers = my_array
you just give the object named my_array another name (my_numbers). So
what you did was just variable aliasing and nothing else.

Please note that in Ruby
var += 5
is exactly the same as
var = var + 5
which binds another name to the variable. This means that
a = 5
b = a
b += 1
[a, b] # => [5, 6]
because the objects didn't change at all -- you just decided to rename
things.

Other languages with value semantics typically create a copy of the
original object when you assign them to anything. This is typically done
in Ruby like
my_array = [1, 2, 3]
my_copy = my_array.clone
which will then give you the behavior you intended:
my_array[0] = :changed
my_array # => [:changed, 2, 3]
my_copy # => [1, 2, 3]

I think that Ruby's behavior is correct because languages where you
explicitly have to specify that you want a reference to an object
instead of a copy of it (like PHP used to do it even for values that are
typically automatically passed by reference in the traditional languages
like C++ and Java) cause more confusion. In Ruby you just give names to
objects and ask them for a copy when you want one. It takes a short
introduction (simply because languages that use pass-by-value somewhere
are more common), but leads to less trouble later.
 
N

Nigel Wilkinson

When you make an assignment b = a, you assign the variable 'b' a
reference to the same array variable 'a' refers to. If you want to
have a copy of array a use dup method like

b = a.dup

Kent.
Thanks everyone

Nigel
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,982
Messages
2,570,190
Members
46,740
Latest member
AdolphBig6

Latest Threads

Top