why one array continues to grow after repeated call

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

  1. Li Chen

    Li Chen Guest

    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
    #1
    1. Advertising

  2. 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
    #2
    1. Advertising

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

    http://talklikeaduck.denhaven2.com/articles/2006/09/13/on-variables-values-and-objects


    --
    Rick DeNatale

    My blog on Ruby
    http://talklikeaduck.denhaven2.com/
     
    Rick DeNatale, Sep 18, 2008
    #3
  4. Li Chen

    Li Chen Guest

    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
    #4
  5. [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
    http://talklikeaduck.denhaven2.com/
     
    Rick DeNatale, Sep 18, 2008
    #5
  6. Li Chen

    Randy Kramer Guest

    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-and-objects


    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.
     
    Randy Kramer, Sep 18, 2008
    #6
  7. > 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
    #7
  8. 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
    #8
  9. Li Chen

    Randy Kramer Guest

    [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.
     
    Randy Kramer, Sep 18, 2008
    #9
  10. 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
    #10
  11. 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
    #11
  12. Li Chen

    Randy Kramer Guest

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


    Yes, that seems helpful.

    > >> 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
    instead.--with apologies to Cicero, et.al.
     
    Randy Kramer, Sep 19, 2008
    #12
  13. Li Chen

    Randy Kramer Guest

    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
    instead.--with apologies to Cicero, et.al.
     
    Randy Kramer, Sep 19, 2008
    #13
  14. 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
    #14
  15. 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
    #15
  16. 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
    #16
  17. Li Chen

    Randy Kramer Guest

    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
    instead.--with apologies to Cicero, et.al.
     
    Randy Kramer, Sep 20, 2008
    #17
  18. Li Chen

    Randy Kramer Guest

    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
    instead.--with apologies to Cicero, et.al.
     
    Randy Kramer, Sep 20, 2008
    #18
  19. [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
    http://talklikeaduck.denhaven2.com/
     
    Rick DeNatale, Sep 20, 2008
    #19
  20. Li Chen

    Li Chen Guest

    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
    #20
    1. Advertising

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

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. ENathan

    code continues after stop debugging

    ENathan, Jun 9, 2005, in forum: ASP .Net
    Replies:
    2
    Views:
    526
    Craig Deelsnyder
    Jun 10, 2005
  2. Chip

    Process continues after stop

    Chip, Feb 9, 2006, in forum: ASP .Net
    Replies:
    4
    Views:
    525
    Mateus Padovani Velloso
    Feb 11, 2006
  3. Mr. SweatyFinger
    Replies:
    2
    Views:
    2,137
    Smokey Grindel
    Dec 2, 2006
  4. TS
    Replies:
    7
    Views:
    1,498
  5. Debo
    Replies:
    3
    Views:
    186
Loading...

Share This Page