# why one array continues to grow after repeated call

Discussion in 'Ruby' started by Li Chen, Sep 18, 2008.

1. ### Li ChenGuest

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-forum.com/.

Li Chen, Sep 18, 2008

2. ### Brian CandlerGuest

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-forum.com/.

Brian Candler, Sep 18, 2008

3. ### Rick DeNataleGuest

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

On Thu, Sep 18, 2008 at 8:41 AM, Li Chen <> 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

--
Rick DeNatale

My blog on Ruby

Rick DeNatale, Sep 18, 2008
4. ### Li ChenGuest

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-forum.com/.

Li Chen, Sep 18, 2008
5. ### Rick DeNataleGuest

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

On Thu, Sep 18, 2008 at 9:27 AM, Li Chen <> 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] # 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

Rick DeNatale, Sep 18, 2008
6. ### Randy KramerGuest

On Thursday 18 September 2008 09:01 am, Rick DeNatale wrote:
> Perhaps this two year old article in my blog might give some insight

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

Randy Kramer, Sep 18, 2008
7. ### Brian CandlerGuest

> 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-forum.com/.

Brian Candler, Sep 18, 2008
8. ### Brian CandlerGuest

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-forum.com/.

Brian Candler, Sep 18, 2008
9. ### Randy KramerGuest

[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).

> 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.

Randy Kramer, Sep 18, 2008
10. ### Sebastian HungereckerGuest

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:
ICQ: 205544826

Sebastian Hungerecker, Sep 18, 2008
11. ### Brian CandlerGuest

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-forum.com/.

Brian Candler, Sep 18, 2008
12. ### Randy KramerGuest

On Thursday 18 September 2008 05:35 pm, Brian Candler wrote:
> 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)

Hmm, don't know how to express this properly (and maybe it's way off
base), but are you hinting at the idea that if I were to try []= on an
immutable data type, then the behavior would not be an assignment. (I'm
not sure what it would do--report an error, create a new object with a
different object_id? (Some quick experiments with a constant seem to
indicate that is the case (actually, it does both).)

That does seem to be what happens at least for this case:

irb(main):224:0> Const = 3.14
(irb):224: warning: already initialized constant Const
=> 3.14
irb(main):225:0> Const.object_id
=> -606494436
irb(main):226:0> Const = 3.00
(irb):226: warning: already initialized constant Const
=> 3.0
irb(main):227:0> Const.object_id
=> -606517176

So now would I say that this is not an assignment? Hmm, I was almost
ready to say yes, but not really--yes, I know it created a new object
instead of changing the value of the old, but the net effect to any
program I've written is that where Const used to be 3.14, it is now
3.00 (I should call this the Indiana legislature assignment).

> But objects mutating is completely different to assignment.

In what practical way? I mean, within the Ruby internals, you're
probably right. If calling it a method instead of an assignment is
only to help me remember that, being a method, it could do something
else, OK, maybe that's helpful, sort of like remembering in C++ that I
could overload (I think) the = operator.

> 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)

Anyway, we don't have to go any further on this point. I'm taking that
free online Ruby course, maybe I'll learn something to change my mind
there. If not, I'll just consider it an assignment till something
bites me--then I'll no doubt (I hope) learn something. ;-)

> 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 :-(

> >> 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;

Thanks, I'll have to let some of that percolate some. ;-)

> 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)

Ok, I guess that goes back to the discussion above--the fact that it is
a different object doesn't (usually?) make a difference in a program I
might write.

Thanks!

Randy Kramer
--
I didn't have time to write a short letter, so I created a video

Randy Kramer, Sep 19, 2008
13. ### Randy KramerGuest

On Thursday 18 September 2008 05:34 pm, Sebastian Hungerecker wrote:
> It accomplishes whatever it has been defined to accomplish. If you do

[]=
> there is no assigment happening unless there's an actual, real

assigmnent
> in []='s method definition. It's not different from any other method

in that
> regard. Consider this:
> class Foo
> def []=(x,y)
> # Do nothing at all
> end
> end
> Foo.new[1]=2 # No assigment whatsoever has taken place.

> HTH,

Sebastien,

Thanks, I think it does, but it still doesn't influence me to think of
it as a method, or to think it's of any practical difference to an
assignment.

In C++, I could overload the = operator (I think) to do something
different than assignment--if I did, I'd just confuse myself (at least
in most circumstances). In Ruby, somebody could make the []= method do
something other than assignment, but that would just be confusing (in
most circumstances), so I'd try to avoid it.

Randy Kramer
--
I didn't have time to write a short letter, so I created a video

Randy Kramer, Sep 19, 2008
14. ### Brian CandlerGuest

Randy Kramer wrote:
> In C++, I could overload the = operator (I think) to do something
> different than assignment

However, you can't in Ruby. "x = y" cannot be made to mean anything
other than "evaluate the expression 'y', and store the resulting
reference in x" (*)

> --if I did, I'd just confuse myself (at least
> in most circumstances). In Ruby, somebody could make the []= method do
> something other than assignment, but that would just be confusing (in
> most circumstances), so I'd try to avoid it.

Here's an example in the standard library of where []= is clearly not
performing an assignment:

irb(main):001:0> str = "abcdefgh"
=> "abcdefgh"
irb(main):002:0> str["cde"]="x"
=> "x"
irb(main):003:0> str
=> "abxfgh"

You can see that here it's a string slicing operation, a mutation of the
string.

In any case, it's nothing to do with the assignment operator. Note that:

x = y # *is not* a method call, and DOES NOT change ANY object
(*)
x[y] = z # *is* a method call, and CAN change object(s)

I don't think I can put it any clearer than that. These really are
completely different. Also, since "x = y" involves no method call, it's
not possible to overload it or change its meaning in any way.

Aside: the [] operator may *look* like looking up an element in an
array, but it is very common for it to be used as something else.

m = lambda { |x,y| x+y }
puts m[3,4] # prints 7

HTH,

Brian.

(*) Note that 'y' could be a local variable, but it could be a method
call.

def y
"hello"
end
x = y
puts x
y = 123
x = y
puts x

And therefore if it's a method call, invoking that method *could* modify
objects as a side-effect.

Whether a bare "y" is a method call or a local variable depends on a
static, parse-time decision. If an *assignment* to a local variable y
was parsed earlier in this scope, then it's a local variable. Note:
"parsed" - not executed. So:

def foo
"hello"
end

if false
foo = "goodbye"
end
puts foo # prints nil

# But you can force the method call interpretation:
puts self.foo
puts foo()

I mention this now because it hopefully it reinforces the need to
recognise a true assignment, versus something which is actually a method
call.
--
Posted via http://www.ruby-forum.com/.

Brian Candler, Sep 19, 2008
15. ### Brian CandlerGuest

Randy Kramer wrote:
> Hmm, don't know how to express this properly (and maybe it's way off
> base), but are you hinting at the idea that if I were to try []= on an
> immutable data type, then the behavior would not be an assignment.

[]= is *never* an assignment.

If you call []= on an object which is immutable, then by definition it
cannot mutate. But there was never any requirement for it to have a
method called []= in the first place, and even if it did, there was no
requirement for it to mutate.

Sure, I can write:

irb(main):001:0> str = "hello"
=> "hello"
irb(main):002:0> str.freeze
=> "hello"
irb(main):003:0> str["h"] = "HHH"
TypeError: can't modify frozen string
from (irb):3:in `[]='
from (irb):3
from :0

but that error is nothing to do with the method being called []=; it's
simply because the string is frozen. I get the same if I do:

irb(main):004:0> str << "foo"
TypeError: can't modify frozen string
from (irb):4:in `<<'
from (irb):4
from :0

But I *can* write:

irb(main):005:0> str = "goodbye"
=> "goodbye"

*That's* an assignment. str now points to a completely different object.
The fact that the old string was frozen is irrelevant. It will be
garbage-collected later, if no-one else also has a reference to it.

> (I'm
> not sure what it would do--report an error, create a new object with a
> different object_id? (Some quick experiments with a constant seem to
> indicate that is the case (actually, it does both).)

Or it could do nothing. Depends what you define []= to do. If no []=
method has been defined, then you'll get an "undefined method" error.

irb(main):002:0> class Foo
irb(main):003:1> end
=> nil
irb(main):004:0> Foo.new[3] = 4
NoMethodError: undefined method `[]=' for #<Foo:0xb7dddecc>
from (irb):4
from :0

Note that you'll get this error whether or not Foo has a "[]" method.
They're entirely independent.

> irb(main):224:0> Const = 3.14
> (irb):224: warning: already initialized constant Const
> => 3.14
> irb(main):225:0> Const.object_id
> => -606494436
> irb(main):226:0> Const = 3.00
> (irb):226: warning: already initialized constant Const
> => 3.0
> irb(main):227:0> Const.object_id
> => -606517176

You've observed that 3.00 and 3.14 are different objects.

"Const" is not an object. At first, Const held a reference to 3.14; and
then it held a reference to 3.00.

That *is* an assignment to Const. However, "Const" itself is not an
object, it's a constant. The name "constant" suggests it shouldn't
change, which is why you get a warning when you assign something
different to it.

> Ok, I guess that goes back to the discussion above--the fact that it is
> a different object doesn't (usually?) make a difference in a program I
> might write.

Here's a case where it does:

# version 1
data = []
10.times { |i| data = "hello" }
data[0] << "x"
p data

# version 2
str = "hello"
data = []
10.times { |i| data = str }
data[0] << "x"
p data

It does catch people out when they write, e.g.

a = Array.new(10, [])

when what they really want is

a = Array.new(10) { [] }

Try both these examples, printing out the object_id of each of the 10
elements of the array, to see how they are different.
--
Posted via http://www.ruby-forum.com/.

Brian Candler, Sep 19, 2008
16. ### Brian CandlerGuest

Just one other thing. All this talk about []= has overlooked the much
more common case of methods called "something=".

Often they happen to perform assignment to an instance variable, e.g.

class Foo
def bar
@bar
end
def bar=(x)
@bar = x
end
end
f = Foo.new
f.bar = 99 # a method call to "bar="
puts f.bar

(There's the "attr_accessor" method to help with this common pattern).

However there's no requirement for them to do so. For example, if you
have a database connection manager class, then you might legitimately
write:

def autocommit=(flag)
if flag
@socket.write("set autocommit on;\n")
else
@socket.write("set autocommit off;\n")
end
end
...
conn.autocommit = true

This is changing state of the connection, but doesn't assign to anything
in Ruby.

Now, there is a subtle trap which I still fall into from time to time.
For example, later in the database connection manager class, you may
write another method which looks like this:

def transaction(sql)
autocommit = false
@socket.write("begin\n")
@socket.write(sql)
@socket.write("commit\n")
ensure
autocommit = true
end

This runs, but unfortunately doesn't do what was intended. "autocommit =
false" just brings a local variable into existence, and sticks false in
it. To call the method called "autocommit=" you have to write:

self.autocommit = false
...
self.autocommit = true

That is, you must always qualify these sorts of method calls with a
receiver, because an unqualified expression of the form "x = ..." is
always taken to be an assignment to a local variable x.

Anyway, just another example of why it's important to distinguish an
assignment from something that isn't

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

Brian Candler, Sep 19, 2008
17. ### Randy KramerGuest

On Friday 19 September 2008 03:02 pm, Brian Candler wrote:
> However, you can't in Ruby. "x = y" cannot be made to mean anything
> other than "evaluate the expression 'y', and store the resulting
> reference in x" (*)

...
> In any case, it's nothing to do with the assignment operator. Note

that:
>
> x = y # *is not* a method call, and DOES NOT change ANY

object
> (*)
> x[y] = z # *is* a method call, and CAN change object(s)

Ok, I think this makes sense to me. Trying to repeat for myself:

* a method (is applied to and) can change an object

* an assignment (evaluates an expression and) stores a reference (to
that expression) in a variable

I'll try to let that sink in ;-)

> I don't think I can put it any clearer than that. These really are
> completely different. Also, since "x = y" involves no method call,

it's
> not possible to overload it or change its meaning in any way.

OK.

> Aside: the [] operator may *look* like looking up an element in an
> array, but it is very common for it to be used as something else.
>
> m = lambda { |x,y| x+y }
> puts m[3,4] # prints 7
>
> HTH,

Brian,

Yup, thanks, I think I'm getting there--I appreciate your patience.
I'll reread all your emails a few times over the next little while.

> (*) Note that 'y' could be a local variable, but it could be a method
> call.
>
> def y
> "hello"
> end
> x = y
> puts x
> y = 123
> x = y
> puts x
>
> And therefore if it's a method call, invoking that method *could*

modify
> objects as a side-effect.
>
> Whether a bare "y" is a method call or a local variable depends on a
> static, parse-time decision. If an *assignment* to a local variable y
> was parsed earlier in this scope, then it's a local variable. Note:
> "parsed" - not executed. So:
>
> def foo
> "hello"
> end
>
> if false
> foo = "goodbye"
> end
> puts foo # prints nil
>
> # But you can force the method call interpretation:
> puts self.foo
> puts foo()
>
> I mention this now because it hopefully it reinforces the need to
> recognise a true assignment, versus something which is actually a

method
> call.

Interesting!

Randy Kramer
--
I didn't have time to write a short letter, so I created a video

Randy Kramer, Sep 20, 2008
18. ### Randy KramerGuest

On Friday 19 September 2008 03:19 pm, Brian Candler wrote:
> Here's a case where it does:
>
> # version 1
> data = []
> 10.times { |i| data = "hello" }
> data[0] << "x"
> p data
>
> # version 2
> str = "hello"
> data = []
> 10.times { |i| data = str }
> data[0] << "x"
> p data

Wow! That's either amazing or frightening (or both).

I did use:

10.times { |i| puts data.object_id}

... to see the object_id of each element in the arrays both above and
below.

(For the record, version 1 creates 10 different objects, version 2
creates only a single object.)

>
> It does catch people out when they write, e.g.
>
> a = Array.new(10, [])
>
> when what they really want is
>
> a = Array.new(10) { [] }
>
> Try both these examples, printing out the object_id of each of the 10
> elements of the array, to see how they are different.

Thanks again (I think ;-)

Randy Kramer
--
I didn't have time to write a short letter, so I created a video

Randy Kramer, Sep 20, 2008
19. ### Rick DeNataleGuest

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

On Thu, Sep 18, 2008 at 2:52 PM, Brian Candler <> wrote:

>
> > (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).
>

They are also called references (actually MRI uses the term value, but
that's probably confusing), because they don't HAVE to be pointers.

To some extent it's implementation dependent. MRI does use pointers for most
object references, but some object references are encoded directly. In MRI,
this happens for things like Fixnums, nil, true, and false. The
implementation detects these since the low-order bit of the reference value
is set, which can be used because all objects which are referenced by
pointers need to have an even address. I hint at this in the referenced
article.

The exact implementation of references/values depends on the particular
implementation of Ruby, so it's best to just think of references as opaque
'tokens' which map 1-1 with object instances.

--
Rick DeNatale

My blog on Ruby

Rick DeNatale, Sep 20, 2008
20. ### Li ChenGuest

Rick Denatale wrote:
> On Thu, Sep 18, 2008 at 9:27 AM, Li Chen <> wrote:
>
>> > 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] # or more clearly just [10, 10, 10, 10]
> end
>
> def method1
> @a1
> end
>
> def method2
> @b1
> end
> end
>

The script in the OP is a simple version of another more lenghy one.

@a1 comprise all the file names from a folder.
@b1 comprise all the file names from another folder.
the construction of @b1 is dependent on @a1.
after initialize I want to check the status of @a1 and @b1.
out of curiossity I call both method1 and method2 more than once.

If I initialize both @a1 and @b1 in initialize method there would many
lines there. In order to keep the method short and easy to read I want
to keep each step in a different method.But I also want to initialize
both of them in initialize method. This is the reason why I put method1
and method2 in initialize method. I guess I misunderstood how method is
called in Ruby. I think I can retrieve the status of @a1 and @b1 by
writing accessor/getter. This way I would not mess up with the method
call.

I didn't realize this post causes so much discussion when I posted. I
really appricate you guys spending your times and explaining the
problem.

Li

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

Li Chen, Sep 20, 2008