[lnkForumImage]
TotalShareware - Download Free Software

Confronta i prezzi di migliaia di prodotti.
Asp Forum
 Home | Login | Register | Search 


 

Forums >

comp.lang.ruby

why one array continues to grow after repeated call

Li Chen

9/18/2008 12:42:00 PM

Hi all,

I write a script for studying purpose:
create a new array(b1) based on an existing one (a1).

I expect that once an array is set up it should be fixed regardless how
many times it is called. But I find array b1 continues to grow if I call
it repeatedly? I can't find an explantion for that. Any idea?

Thank you very much in advance,

Li



C:\Users\Alex>irb
irb(main):001:0> class A
irb(main):002:1> def initialize
irb(main):003:2> @a1=[]
irb(main):004:2> @b1=[]
irb(main):005:2> method1
irb(main):006:2> end
irb(main):007:1>
irb(main):008:1* def method2
irb(main):009:2> (method1.size).times{@b1<<10}
irb(main):010:2> return @b1
irb(main):011:2> end
irb(main):012:1>
irb(main):013:1*
irb(main):014:1* def method1
irb(main):015:2> @a1=[1,2,3,4]
irb(main):016:2> return @a1
irb(main):017:2> end
irb(main):018:1>
irb(main):019:1* end
=> nil
irb(main):020:0> ob=A.new
=> #<A:0x2f9f7c @b1=[], @a1=[1, 2, 3, 4]>
irb(main):021:0> ob.method2
=> [10, 10, 10, 10]
irb(main):022:0> ob.method2
=> [10, 10, 10, 10, 10, 10, 10, 10]
irb(main):023:0> ob.method2
=> [10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10]
irb(main):024:0> ob.method1
=> [1, 2, 3, 4]
irb(main):025:0> ob.method2
=> [10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10]
irb(main):026:0> exit
--
Posted via http://www.ruby-....

20 Answers

Brian Candler

9/18/2008 12:59:00 PM

0

Li Chen wrote:
> I expect that once an array is set up it should be fixed regardless how
> many times it is called.

I'm not sure why you expect that.

In your program, you first assign @b1 to an empty array.

In method2, you loop around 4 times pushing '10' onto the end of the
array. Therefore @b1 becomes, each time around the loop,
[10]
[10,10]
[10,10,10]
[10,10,10,10]

If you call method2 again, then again 4 times you push 10 onto the end
of the array, so after that you get
[10,10,10,10,10,10,10,10]

Why would you expect the first four pushes to work, but the second 4
pushes not to work?
--
Posted via http://www.ruby-....

Rick DeNatale

9/18/2008 1:02:00 PM

0

[Note: parts of this message were removed to make it a legal post.]

On Thu, Sep 18, 2008 at 8:41 AM, Li Chen <chen_li3@yahoo.com> wrote:

> Hi all,
>
> I write a script for studying purpose:
> create a new array(b1) based on an existing one (a1).
>
> I expect that once an array is set up it should be fixed regardless how
> many times it is called. But I find array b1 continues to grow if I call
> it repeatedly? I can't find an explantion for that. Any idea?
>
> Thank you very much in advance,
>

You seem to have a common misperception that assigning a value to a ruby
variable acts like a declaration, rather than just a, potentially temporary,
binding of the variable to a particular object.

> C:\Users\Alex>irb
> irb(main):001:0> class A
> irb(main):002:1> def initialize
> irb(main):003:2> @a1=[]
> irb(main):004:2> @b1=[]


If instance variables were constrained by the declaration, then @a1 and @b1
would have to always refer to empty arrays.

>
> irb(main):005:2> method1

And method1 could have no effect here.

>
> irb(main):006:2> end
> irb(main):007:1>
> irb(main):008:1* def method2
> irb(main):009:2> (method1.size).times{@b1<<10}


What really happens here is that the array referenced by @b1 has @a1.size
10's added each time the method is called.

>
> irb(main):010:2> return @b1
> irb(main):011:2> end
> irb(main):012:1>
> irb(main):013:1*
> irb(main):014:1* def method1
> irb(main):015:2> @a1=[1,2,3,4]

And this line replaces the empty array bound to @a1 in the initialize method
with a new array instance. The original empty array is now subject to
garbage collection.

>
> irb(main):016:2> return @a1
> irb(main):017:2> end
> irb(main):018:1>
> irb(main):019:1* end
> => nil
> irb(main):020:0> ob=A.new
> => #<A:0x2f9f7c @b1=[], @a1=[1, 2, 3, 4]>
> irb(main):021:0> ob.method2
> => [10, 10, 10, 10]
> irb(main):022:0> ob.method2
> => [10, 10, 10, 10, 10, 10, 10, 10]
> irb(main):023:0> ob.method2
> => [10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10]
> irb(main):024:0> ob.method1
> => [1, 2, 3, 4]
> irb(main):025:0> ob.method2
> => [10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10]
> irb(main):026:0> exit


Perhaps this two year old article in my blog might give some insight

http://talklikeaduck.denh...articles/2006/09/13/on-variables-values-a...


--
Rick DeNatale

My blog on Ruby
http://talklikeaduck.denh...

Li Chen

9/18/2008 1:28:00 PM

0

Brian Candler wrote:
> Li Chen wrote:
>> I expect that once an array is set up it should be fixed regardless how
>> many times it is called.
>
> I'm not sure why you expect that.
>
> In your program, you first assign @b1 to an empty array.
>
> In method2, you loop around 4 times pushing '10' onto the end of the
> array. Therefore @b1 becomes, each time around the loop,
> [10]
> [10,10]
> [10,10,10]
> [10,10,10,10]

How come this loop happan? I don't want a loop here.

--
Posted via http://www.ruby-....

Rick DeNatale

9/18/2008 2:34:00 PM

0

[Note: parts of this message were removed to make it a legal post.]

On Thu, Sep 18, 2008 at 9:27 AM, Li Chen <chen_li3@yahoo.com> wrote:

> Brian Candler wrote:
> > Li Chen wrote:
> >> I expect that once an array is set up it should be fixed regardless how
> >> many times it is called.
> >
> > I'm not sure why you expect that.
> >
> > In your program, you first assign @b1 to an empty array.
> >
> > In method2, you loop around 4 times pushing '10' onto the end of the
> > array. Therefore @b1 becomes, each time around the loop,
> > [10]
> > [10,10]
> > [10,10,10]
> > [10,10,10,10]
>
> How come this loop happan? I don't want a loop here.
>

Because that's what you coded:

irb(main):008:1* def method2
irb(main):009:2> (method1.size).times{@b1<<10}
irb(main):010:2> return @b1
irb(main):011:2> end

$ qri Integer#times
---------------------------------------------------------- Integer#times
int.times {|i| block } => int
------------------------------------------------------------------------
Iterates block int times, passing in values from zero to int - 1.

It's not really clear what you are trying to do with your two methods, why
not just initialize both instance variable in the intialize method?

class A
def initialize
@a1 = [1,2,3,4]
@b1 = [10].@a1.length # or more clearly just [10, 10, 10, 10]
end

def method1
@a1
end

def method2
@b1
end
end

I'll leave discussion about using intention revealing method and variable
names for another time.

--
Rick DeNatale

My blog on Ruby
http://talklikeaduck.denh...

Randy Kramer

9/18/2008 4:57:00 PM

0

On Thursday 18 September 2008 09:01 am, Rick DeNatale wrote:
> Perhaps this two year old article in my blog might give some insight
>http://talklikeaduck.denhaven2.com/articles/2006/09/13/on-variables-values-a...

I'm not the OP but I took a quick glance at the article. Sometime when
I have more time to study it, it may help me a lot, but I had some
trouble with it, and comments about it. I would have left them on that
page but it appears comments are disabled.

The area where I had my first problem was around here:

<quote>
Mutability, and Aliasing

Here's one of those stumbling blocks for those who expect variables in a
uniformly object-oriented language to work like they do in a language
like C or Fortran:

1: a = [1, 2, 3]
2: b = [1, 2, 3]
3: c = a

4: a[1] = 0

5: p a #=> [1, 0, 3]
6: p b #=> [1, 2, 3]
7: p c #=> [1, 0, 3]

...

Line 4 might look like an assignment to the variable a, but it's really
a method call to the array which a happens to be referencing at the
time. And that method (called []=) changes, or mutates that array. That
change will be visible through the variables a, c and any others that
reference that particular array. Multiple references to the same object
are called aliases to that object. They might be named variables, or
referenced which are inside another object:
</quote>

The statement "Line 4 might look like an assignment to the variable a,
but it's really a method call to the array which a happens to be
referencing at the time." is almost like a red herring for me--it's
really not (imho) the thing that is important at that moment in your
example.

The really important thing is the previous statement, and how it works
(and asignment in general, whether you call it a method or whatever):

3: c = a

To me, the thing that is interesting (and finally sinking in) is how
assignments work. (I'm not sure whether I've read this before
somewhere or not.)

What I think will help me is recognizing (I think this is true) that
assignments work in different ways, as follows:

If you assign an object to a variable (like a = [1, 2, 3]), the
assignment works as expected (or at least as most of us expect, I
think ;-)

(I know that's not completely true at the level of Ruby internals--a
isn't the memory containing the array [1, 2, 3] but instead in some (as
you say on the page) pedagological (sp?) sense, points to the memory
area containing the array [1, 2, 3].)

In contrast, when you (attempt to) assign a variable to a variable, Ruby
does something that is a little surprising (at least to me, and perhaps
to others coming from, for example C). Instead of assigning a (the 2nd
variable) to c (the 1st variable), Ruby seaches out the object to which
the variable "points" (the object which the variable references--the
array [1, 2, 3]), and assigns that object to c.

I'm not a C programmer (have tried to learn at times), but it is almost
like the Ruby assignment a = c is handled more like (hmm, I'm trying to
think of the right C syntax, would it be):

c = *a (or would that be c = &a--darn)

(So, at first glance, c is automatically something like a pointer. I
guess in Ruby terms, that's what is called an alias.)

Furthermore, I suspect that you can't just do anything you want (like
you might in C) with that new pointer to a--what do I mean? Well, c
doesn't have any notion of a anymore--c is a pointer (reference) to
what a contained, but it is "direct", it doesn't go through a in any
sense after the assignment. (What could I do with it in c--can't think
of anything atm.)

Ok, I guess that's why it's called an alias--it's another name for that
object.

Anyway, I think this will help me, unless it collides with reality at
some point.

Am I totally off base?

Randy Kramer
--
I didn't have time to write a short letter, so I created a video
instead.--with apologies to Cicero, et.al.

Brian Candler

9/18/2008 6:52:00 PM

0

> The statement "Line 4 might look like an assignment to the variable a,
> but it's really a method call to the array which a happens to be
> referencing at the time." is almost like a red herring for me--it's
> really not (imho) the thing that is important at that moment in your
> example.

It's crucial. x[y] = z "looks" very much like some sort of an
assignment. BUT IT ISN'T AT ALL.

It is exactly the same as: x.[]=(y,z)

or:

x.send(:[]=, y, z)

(you get into smiley programming if you do that :-) It's a method call
to object x; the method has the ungainly name "[]="; and the arguments
are y,z

> The really important thing is the previous statement, and how it works
> (and asignment in general, whether you call it a method or whatever):
>
> 3: c = a
>
> To me, the thing that is interesting (and finally sinking in) is how
> assignments work. (I'm not sure whether I've read this before
> somewhere or not.)

Yes, that's very important too. You are assigning to a local variable,
which is just a slot on the stack. A local variable is in fact one of
the few things which is *not* an object in Ruby. It *holds* a reference
to an object, but is not an object itself.

> What I think will help me is recognizing (I think this is true) that
> assignments work in different ways, as follows:
>
> If you assign an object to a variable (like a = [1, 2, 3]), the
> assignment works as expected (or at least as most of us expect, I
> think ;-)
>
> (I know that's not completely true at the level of Ruby internals--a
> isn't the memory containing the array [1, 2, 3] but instead in some (as
> you say on the page) pedagological (sp?) sense, points to the memory
> area containing the array [1, 2, 3].)

In a quite accurate sense, 'a' is a pointer to the memory containing the
array [1, 2, 3]

> In contrast, when you (attempt to) assign a variable to a variable, Ruby
> does something that is a little surprising (at least to me, and perhaps
> to others coming from, for example C). Instead of assigning a (the 2nd
> variable) to c (the 1st variable), Ruby seaches out the object to which
> the variable "points" (the object which the variable references--the
> array [1, 2, 3]), and assigns that object to c.

No. It simply copies a to c. a contains a pointer to the array, and so
now c also contains a pointer to the same array. It's really that
simple.

> I'm not a C programmer (have tried to learn at times), but it is almost
> like the Ruby assignment a = c is handled more like (hmm, I'm trying to
> think of the right C syntax, would it be):
>
> c = *a (or would that be c = &a--darn)

No, it's "c = a" in C as well.

char *a;
char *c;

a = malloc(123); /* a points to some memory */
c = a; /* c points to the same memory */

> (So, at first glance, c is automatically something like a pointer. I
> guess in Ruby terms, that's what is called an alias.)

They're called "references", to distinguish them from pointers because
you can't actually use them as pointers (e.g. you can't "increment" the
pointer to point to the next area of memory, as you can in C).

Aliases are just multiple references/pointers to the same thing. But an
object has no idea how many of these may or may not exist.

> Furthermore, I suspect that you can't just do anything you want (like
> you might in C) with that new pointer to a--what do I mean? Well, c
> doesn't have any notion of a anymore--c is a pointer (reference) to
> what a contained, but it is "direct", it doesn't go through a in any
> sense after the assignment. (What could I do with it in c--can't think
> of anything atm.)

You can do exactly the same with a and c. They are exactly equal in
contents and power.

a.first
c.first

both invoke the method "first" on the same object.

HTH,

Brian.
--
Posted via http://www.ruby-....

Brian Candler

9/18/2008 7:17:00 PM

0

Oh I forgot to add - while playing about with this in irb, the method
"object_id" is really useful. It returns the actual pointer. (Well, it's
not *exactly* the pointer; it's a pointer which has been cleverly
encoded to include some information about the type of the object, to
optimise some common operations, but internally Ruby is able to map the
object_id to the actual memory location with some simple bit-masking)

irb(main):001:0> a = "hello"
=> "hello"
irb(main):002:0> b = a
=> "hello"
irb(main):003:0> a.object_id
=> -605500598
irb(main):004:0> b.object_id
=> -605500598
irb(main):005:0> a << " world"
=> "hello world"
irb(main):006:0> a
=> "hello world"
irb(main):007:0> b
=> "hello world"

Here, you can see that a and b really are pointers to the same object.
You could have done `b << " world"` instead of `a << " world"` and got
exactly the same effect. In both cases you are invoking method "<<" with
argument " world" on the object at "memory location"(ish) -605500598.

Contrast with:

irb(main):008:0> a = "hello"
=> "hello"
irb(main):009:0> b = "hello"
=> "hello"
irb(main):010:0> a.object_id
=> -605560548
irb(main):011:0> b.object_id
=> -605574918
irb(main):012:0> a << " world"
=> "hello world"
irb(main):013:0> a
=> "hello world"
irb(main):014:0> b
=> "hello"

You can see clearly that a and b point to different strings, which
happen to start out with the same content.

Incidentally, if a points to a string and you want b to point to a
different string with the same content, you can do

b = a.dup

or

b = String.new(a)

This creates a new String object, and copies the content byte by byte
from the old one into the new one, leaving b pointing at the new one. In
practice this doesn't need to be done very much.

Finally, note that if you write

a += " world"

this is short for

a = a + " world"

Now the expression on the RHS creates a new string object, being the
concatenation of the original string and " world", and then this pointer
is stored in a; a no longer points to what it did before. But assigning
to a doesn't affect any other local variable which points to the
original string. So:

irb(main):018:0> a = "hello"
=> "hello"
irb(main):019:0> b = a
=> "hello"
irb(main):020:0> a.object_id
=> -605642908
irb(main):021:0> b.object_id
=> -605642908
irb(main):022:0> a += " world"
=> "hello world"
irb(main):023:0> a.object_id
=> -605661638
irb(main):024:0> b.object_id
=> -605642908
irb(main):025:0> a
=> "hello world"
irb(main):026:0> b
=> "hello"

You can see that a is pointing to a new object, whereas b is still
pointing to the original one.

I hope this makes things clearer rather than muddier :-)

Regards,

Brian.
--
Posted via http://www.ruby-....

Randy Kramer

9/18/2008 9:02:00 PM

0

[Note: parts of this message were removed to make it a legal post.]

On Thursday 18 September 2008 02:52 pm, Brian Candler wrote:
> It's crucial. x[y] = z "looks" very much like some sort of an
> assignment. BUT IT ISN'T AT ALL.
>
> It is exactly the same as: x.[]=(y,z)
>
> or:
>
> x.send(:[]=, y, z)
>
> (you get into smiley programming if you do that :-) It's a method call
> to object x; the method has the ungainly name "[]="; and the arguments
> are y,z

My comment on assignment was sort of a digression (not my main point), but, nevertheless, I can do the following while using object_id (as suggested in your followup) to confirm that I'm still dealing with the same object. (a was leftover from some previous "exercise"):

irb(main):098:0> a
=> [nil, nil, 5, nil, nil, nil, nil, nil, nil, "testy", "foo"]
irb(main):099:0> a.object_id
=> -605519610
irb(main):100:0> a[2] = 3
=> 3
irb(main):101:0> a
=> [nil, nil, 3, nil, nil, nil, nil, nil, nil, "testy", "foo"]
irb(main):102:0> a.object_id
=> -605519610
irb(main):103:0> a.[]=(2,4)
=> 4
irb(main):104:0> a
=> [nil, nil, 4, nil, nil, nil, nil, nil, nil, "testy", "foo"]
irb(main):105:0> a.object_id
=> -605519610
irb(main):106:0> a.send(:[]=, 2, 5)
=> 5
irb(main):107:0> a
=> [nil, nil, 5, nil, nil, nil, nil, nil, nil, "testy", "foo"]
irb(main):108:0> a.object_id
=> -605519610

So, what is the net effect? With any of the three syntaxes (a[2] = 3, a.[]=(2,4), a.send(:[]=, 2, 5)), I change (assign a new value to) the 3rd element of the array a.

What is the practical point of saying it is not an assignment? Yes, I know that the underlying thing is a method, and somehow a[2] = 3 is syntactic sugar (I guess), for a.[]=(2,3), but in the end, it accomplishes an assignment.

I'm not intentionally trying to be obstinate, it just seems there is no practical impact to what happens, or to my thinking process as a programmer, to not consider a[2] = 3 an assignment.

> Yes, that's very important too. You are assigning to a local variable,
> which is just a slot on the stack. A local variable is in fact one of
> the few things which is *not* an object in Ruby. It *holds* a reference
> to an object, but is not an object itself.

I'm assuming the same is true for any variable in Ruby, not just local variables?

> > I'm not a C programmer (have tried to learn at times), but it is almost
> > like the Ruby assignment a = c is handled more like (hmm, I'm trying to
> > think of the right C syntax, would it be):
> >
> > c = *a (or would that be c = &a--darn)
>
> No, it's "c = a" in C as well.
>
> char *a;
> char *c;
>
> a = malloc(123); /* a points to some memory */
> c = a; /* c points to the same memory */

Ok, is there still some magic here?. Compare (thinking in C):

Given:

char *a;
char *c;

Would both of these assignments (in C) work as they would in Ruby:

a = [1, 2, 3]
c = a

... or would the first one have to be something like:

a = &([1, 2, 3])

> They're called "references", to distinguish them from pointers because
> you can't actually use them as pointers (e.g. you can't "increment" the
> pointer to point to the next area of memory, as you can in C).

Ahh, that's helpful, I've often wondered what made a pointer a reference instead.

> Aliases are just multiple references/pointers to the same thing. But an
> object has no idea how many of these may or may not exist.

Ok. And it's the same in C for pointers, iiuc.

> HTH,
Yes, thanks!

Randy Kramer
--
I didn't have time to write a short letter, so I created a video instead.--with apologies to Cicero, et.al.

Sebastian Hungerecker

9/18/2008 9:35:00 PM

0

Randy Kramer wrote:
> What is the practical point of saying it is not an assignment? =A0Yes, I =
know
> that the underlying thing is a method, and somehow a[2] =3D 3 is syntactic
> sugar (I guess), for a.[]=3D(2,3), but in the end, it accomplishes an
> assignment.

It accomplishes whatever it has been defined to accomplish. If you do []=3D=
=20
there is no assigment happening unless there's an actual, real assigmnent
in []=3D's method definition. It's not different from any other method in t=
hat=20
regard. Consider this:
class Foo
def []=3D(x,y)
# Do nothing at all
end
end
=46oo.new[1]=3D2 # No assigment whatsoever has taken place.

HTH,
Sebastian
=2D-=20
Jabber: sepp2k@jabber.org
ICQ: 205544826

Brian Candler

9/18/2008 9:35:00 PM

0

Randy Kramer wrote:
> So, what is the net effect? With any of the three syntaxes (a[2] = 3,
> a.[]=(2,4), a.send(:[]=, 2, 5)), I change (assign a new value to) the
> 3rd element of the array a.
>
> What is the practical point of saying it is not an assignment?

It just so happens that the semantics of the []= method on an Array
object are to replace the x'th element with a pointer to y. However
there is no requirement for any object to behave in such a way.

class Foo
def []=(x,y)
puts "Hello #{x}, today is #{y}"
end
end
a = Foo.new
a["Randy"] = "Friday"

It's just a method call. Objects can, in response to method calls,
mutate (normally that means change what their instance variables point
to; in the case of Array, which is a special built-in class, it means
change the underlying hidden data structure)

But objects mutating is completely different to assignment.

var = expr

means calculate the value of 'expr' (which *always* results in a
reference to some object), and store this reference in the local
variable 'var' (which is a slot on the stack)

This is something I find great about Ruby: everything is a reference. In
C you have to decide whether you are passing a value or a pointer; in
C++ you have to decide whether you are passing a value, a pointer or a
reference :-(

> Yes, I
> know that the underlying thing is a method, and somehow a[2] = 3 is
> syntactic sugar (I guess), for a.[]=(2,3), but in the end, it
> accomplishes an assignment.

In the case of an Array object, it accomplishes a modification to that
object's internal state.

>> Yes, that's very important too. You are assigning to a local variable,
>> which is just a slot on the stack. A local variable is in fact one of
>> the few things which is *not* an object in Ruby. It *holds* a reference
>> to an object, but is not an object itself.
>
> I'm assuming the same is true for any variable in Ruby, not just local
> variables?

Sure: global variables, instance variables and class variables all live
in different places, but they are just holders of references and are not
objects in themselves.

>> a = malloc(123); /* a points to some memory */
>> c = a; /* c points to the same memory */
>
> Ok, is there still some magic here?. Compare (thinking in C):
>
> Given:
>
> char *a;
> char *c;
>
> Would both of these assignments (in C) work as they would in Ruby:
>
> a = [1, 2, 3]
> c = a

You can't do exactly that in C. One possibility is

char a[3] = { 1, 2, 3 };
char *c;

c = a;

Here a and c are not exactly the same. Arrays in C are just pointers to
memory, and you could use 'a' and 'c' in any context that a 'char *'
pointer would be expected, but whilst you can modify 'c' to point
somewhere else, you cannot modify 'a' (e.g. a++ is illegal, as is a = b)

A bit closer to Ruby is:

char *a;
char *c;

a = malloc(3);
memcpy(a, "\x01\x02\x03", 3);

c = a;

Note that in Ruby, the expression [1, 2, 3] creates a new object every
time it executes. Try for example,

10.times { puts [1, 2, 3].object_id }

The same even applies for Strings:

10.times { puts "Hello".object_id }

There are only a few cases where you get the same object each time round
(e.g. Fixnums, some Regexp literals, symbols, nil, true, false)

B.
--
Posted via http://www.ruby-....