how to avoid passing by reference and how to copy objects

Discussion in 'Ruby' started by Adam Akhtar, Aug 27, 2008.

  1. Adam Akhtar

    Adam Akhtar Guest

    Hi after a bit of searching and reading im quite confused by this.

    I first learnt C so used to being able to pass by reference or by value.
    In Ruby its just by reference (so ive just found out after an hour of
    chasing a bug).

    I have an array of ojbects which are baisically acting like structures.
    I want to pass the array to a function. This function will take the
    array, make a new copy so as to protect the original from any changes.
    Changes will be made to the copy and then the copy is returned.

    How do i do that?

    Ive looked dup but i dont think it works for objects... isnt there a
    method to copy an object (i.e make new space in memory and put the data
    there rather than juts pointing to the original objects address).

    Id have thought it was necessary if you can only pass by reference...or
    is the above considered bad design practice in ruby?
    --
    Posted via http://www.ruby-forum.com/.
    Adam Akhtar, Aug 27, 2008
    #1
    1. Advertising

  2. On Wed, Aug 27, 2008 at 9:10 AM, Adam Akhtar <> wrote:
    > Hi after a bit of searching and reading im quite confused by this.
    >
    > I first learnt C so used to being able to pass by reference or by value.
    > In Ruby its just by reference (so ive just found out after an hour of
    > chasing a bug).
    >
    > I have an array of ojbects which are baisically acting like structures.
    > I want to pass the array to a function. This function will take the
    > array, make a new copy so as to protect the original from any changes.
    > Changes will be made to the copy and then the copy is returned.
    >
    > How do i do that?


    The problem with the above approach is that an array holds also references
    to the contained objects. So if you are changing what objects the array contains
    your approach is fine. If you are changing the contained objects
    themselves, then
    you have the same problem, since dup makes a shallow copy:


    irb(main):001:0> a = %w{one two three}
    => ["one", "two", "three"]
    irb(main):002:0> a.map {|x| x.object_id}
    => [-606229548, -606229578, -606229608]
    irb(main):003:0> b = a.dup
    => ["one", "two", "three"]
    irb(main):004:0> b[0] = "new value"
    => "new value"
    irb(main):005:0> a
    => ["one", "two", "three"]
    irb(main):006:0> b
    => ["new value", "two", "three"]
    irb(main):007:0> b.map {|x| x.object_id}
    => [-606260528, -606229578, -606229608]

    As you can see here, both a and b are referencing the same
    string objects in their second and third position. So the strings
    "two" and "three" are the same objects in both arrays. If you
    change *those objects* the change will be reflected in both
    arrays:

    irb(main):008:0> a[2].upcase!
    => "THREE"
    irb(main):009:0> a
    => ["one", "two", "THREE"]
    irb(main):010:0> b
    => ["new value", "two", "THREE"]

    If you want to avoid this, you need b to be a deep copy of a.
    One way to achieve it is with Marshal:

    irb(main):011:0> c = Marshal.load(Marshal.dump(a))
    => ["one", "two", "THREE"]
    irb(main):012:0> c.map {|x| x.object_id}
    => [-606348068, -606348078, -606348088]
    irb(main):014:0> a[2].downcase!
    => "three"
    irb(main):015:0> a
    => ["one", "two", "three"]
    irb(main):016:0> b
    => ["new value", "two", "three"]
    irb(main):017:0> c
    => ["one", "two", "THREE"]

    Although you should read a bit more on Marshal to understand if
    it suits your needs.

    Hope this helps,

    Jesus.
    Jesús Gabriel y Galán, Aug 27, 2008
    #2
    1. Advertising

  3. Adam Akhtar

    Thomas B. Guest

    Adam Akhtar wrote:
    > Hi after a bit of searching and reading im quite confused by this.
    >
    > I first learnt C so used to being able to pass by reference or by value.
    > In Ruby its just by reference (so ive just found out after an hour of
    > chasing a bug).
    >
    > I have an array of ojbects which are baisically acting like structures.
    > I want to pass the array to a function. This function will take the
    > array, make a new copy so as to protect the original from any changes.
    > Changes will be made to the copy and then the copy is returned.
    >
    > How do i do that?
    >
    > Ive looked dup but i dont think it works for objects... isnt there a
    > method to copy an object (i.e make new space in memory and put the data
    > there rather than juts pointing to the original objects address).
    >
    > Id have thought it was necessary if you can only pass by reference...or
    > is the above considered bad design practice in ruby?


    Have a look at this post:
    http://al2o3-cr.blogspot.com/2008/08/object-arr.html it explains your
    case a bit.
    --
    Posted via http://www.ruby-forum.com/.
    Thomas B., Aug 27, 2008
    #3
  4. On 27.08.2008 09:10, Adam Akhtar wrote:

    > I first learnt C so used to being able to pass by reference or by value.
    > In Ruby its just by reference (so ive just found out after an hour of
    > chasing a bug).


    Well, it's actually an advantage because passing a reference is usually
    faster than passing an object by value.

    > I have an array of ojbects which are baisically acting like structures.
    > I want to pass the array to a function. This function will take the
    > array, make a new copy so as to protect the original from any changes.
    > Changes will be made to the copy and then the copy is returned.
    >
    > How do i do that?
    >
    > Ive looked dup but i dont think it works for objects... isnt there a
    > method to copy an object (i.e make new space in memory and put the data
    > there rather than juts pointing to the original objects address).
    >
    > Id have thought it was necessary if you can only pass by reference...or
    > is the above considered bad design practice in ruby?


    Adding to the excellent advice: depending on what you have in your
    structs you can also use Array#map to get a copy like this:

    def f(x)
    copy = x.map {|el| el.dup}
    end

    or even

    def f(x)
    x.map do |el|
    # your calculation on each el
    YourStruct.new some, more, values, like, el.length + 1
    end
    end

    As I said, whether it's safe depends on your data.

    But I fully agree to Brian's remark that something like this (deep copy)
    is usually not done. It depends on your problem at hand which is the
    best approach so if you provide a little more detail we might be able to
    provide more concrete advice how to tackle your particular situation.

    Kind regards

    robert
    Robert Klemme, Aug 27, 2008
    #4
  5. Hi --

    On Wed, 27 Aug 2008, Adam Akhtar wrote:

    > Hi after a bit of searching and reading im quite confused by this.
    >
    > I first learnt C so used to being able to pass by reference or by value.
    > In Ruby its just by reference (so ive just found out after an hour of
    > chasing a bug).


    I know this sounds pedantic, but I think it's really pass by value,
    with the values being references.

    In other words, if you do this:

    a = [1,2,3]

    you're binding the identifier a to a reference to the array. If you
    then do:

    puts a

    you're passing the value of a (which is a reference to an array) to
    puts.

    In practical terms it doesn't matter, but it helps make sense of, for
    example, the fact that you can't pass a reference-by-reference.


    David

    --
    Rails training from David A. Black and Ruby Power and Light:
    Intro to Ruby on Rails January 12-15 Fort Lauderdale, FL
    Advancing with Rails January 19-22 Fort Lauderdale, FL
    See http://www.rubypal.com for details and updates!
    David A. Black, Aug 27, 2008
    #5
  6. Adam Akhtar

    Adam Akhtar Guest

    thanks everyone for that.

    I typed along on irb to the examples presented and understand more about
    rubys behaviour with "copying" stuff.

    Its basically like (probably IS under the hood) Pointers.
    so for some arrays a and b

    a = b

    thats basically copying the pointers themselves. So whatever changes i
    make in A will be reflected in B.

    a = b.dup

    create some NEW pointers but which point to the same memory space. If i
    edit one of the members it will be reflected in both a and b. However if
    i substitute it for a totally different member im guessing new memory
    space is allocated and a pointer points to that new memory space. Hence
    the change is reflected in only one array.

    to get a copy where new memory space for duplicate objects is creted,
    you look towards marshall..

    am i right (roughly) ... hmmm pointers in C was always tricky.
    --
    Posted via http://www.ruby-forum.com/.
    Adam Akhtar, Aug 28, 2008
    #6
  7. Adam Akhtar

    Lex Williams Guest

    > create some NEW pointers but which point to the same memory space. If i
    > edit one of the members it will be reflected in both a and b. However if
    > i substitute it for a totally different member im guessing new memory
    > space is allocated and a pointer points to that new memory space. Hence
    > the change is reflected in only one array.
    >
    > to get a copy where new memory space for duplicate objects is creted,
    > you look towards marshall..
    >
    > am i right (roughly) ... hmmm pointers in C was always tricky.


    Here is a little example

    class XTest

    attr_accessor :test

    def initialize
    @test = 2
    end


    end

    first = XTest.new
    first.test = 10
    second = first.clone
    second.test = 11
    puts first.test

    So , If you want to preserve your parameters , clone them.
    --
    Posted via http://www.ruby-forum.com/.
    Lex Williams, Aug 28, 2008
    #7
  8. Adam Akhtar

    Adam Akhtar Guest

    thanks lex for that.
    Im going to search for "ruby program design principles" or patterns
    /best practices with regards to functions. When i learnt C (10 years ago
    so its rusty) I was told to use pass by reference only when necessary. I
    think the reason being bug tracking etc maybe the reasoning was similiar
    ot that of not to over/misuse global variables.

    So switching to ruby where pass by reference is the only choice seems to
    be making my programming a little less smooth. Im just used to knowing
    that the original value wont be changed. Sure I could dup or clone
    everything but that seems to go against what ruby intends.

    if anyone has a good link or book regarding such differences, their
    implications and how to design along them rather than against (i.e. best
    practice) please give us a shout. In the meantime ill go away and do
    some searching and rejig my code.

    thanks
    --
    Posted via http://www.ruby-forum.com/.
    Adam Akhtar, Aug 28, 2008
    #8
  9. Hi --

    On Thu, 28 Aug 2008, Adam Akhtar wrote:

    > thanks lex for that.
    > Im going to search for "ruby program design principles" or patterns
    > /best practices with regards to functions. When i learnt C (10 years ago
    > so its rusty) I was told to use pass by reference only when necessary. I
    > think the reason being bug tracking etc maybe the reasoning was similiar
    > ot that of not to over/misuse global variables.
    >
    > So switching to ruby where pass by reference is the only choice seems to
    > be making my programming a little less smooth. Im just used to knowing
    > that the original value wont be changed. Sure I could dup or clone
    > everything but that seems to go against what ruby intends.


    See my earlier post, and Brian's last one. It's not pass by reference;
    it's pass by value, where the values are references. That's actually
    good news. For one thing, you don't have to do (indeed can't do)
    pointer arithmetic. Every reference is exactly one step away from its
    object. There are no references to references (which is why you can't
    do pass-by-reference in Ruby).

    C is actually a poor point of reference (ha ha) for understanding Ruby
    variables and references, because the two are so different. I'd
    encourage you not to worry about how Ruby semantics align with C
    semantics, because they pretty much don't. In C you have this:


    ***p -> **p -> *p -> p

    but in Ruby you always have this:

    b
    |
    v

    a-> obj <- c

    ^
    |
    d


    David

    --
    Rails training from David A. Black and Ruby Power and Light:
    Intro to Ruby on Rails January 12-15 Fort Lauderdale, FL
    Advancing with Rails January 19-22 Fort Lauderdale, FL
    See http://www.rubypal.com for details and updates!
    David A. Black, Aug 28, 2008
    #9
  10. Adam Akhtar

    Xavier Noria Guest

    On Thu, Aug 28, 2008 at 1:03 PM, David A. Black <> wrote:

    > C is actually a poor point of reference (ha ha) for understanding Ruby
    > variables and references, because the two are so different. I'd
    > encourage you not to worry about how Ruby semantics align with C
    > semantics


    Nevertheless C is only pass by value. That could help.

    There's no way in C to accomplish this

    a = 7;
    some_function(a);
    // a is 7 for certain here

    You can pass the address of a:

    a = 7;
    p = &a;
    some_other_function(p);
    // a may be 5 now

    but note that the argument of the function is p, it is not a. So you
    are clearly just passing p by value.

    Again, when some_other_function returns there's no way p is pointing
    to anything that is not a, because of pass by value semantics. In some
    vague sense this second example has some resemblances to Ruby: you are
    always passing ps in Ruby so to speak (that's an analogy, it is clear
    from the rest of the thread there are no actual pointers involved).

    Through those ps, if the object is mutable you may change the state of
    the object, but cannot change the very reference, the object pointed
    at by p, the same way you cannot in C.

    Java is pass by value as well.
    Xavier Noria, Aug 28, 2008
    #10
  11. Adam Akhtar

    Xavier Noria Guest

    On Thu, Aug 28, 2008 at 1:32 PM, Xavier Noria <> wrote:

    > There's no way in C to accomplish this
    >
    > a = 7;
    > some_function(a);
    > // a is 7 for certain here


    Sorry I messed up the wording, I mean there's no way some_function can
    change the value of a in the caller.
    Xavier Noria, Aug 28, 2008
    #11
  12. Adam Akhtar

    Xavier Noria Guest

    Perl is only pass-by-reference by the way, sometimes it helps to see a
    pass-by-reference example. So in Perl you can do this:

    sub some_function { $_[0] = 5 }

    my $a = 7;
    some_function($a);
    # now $a is 5

    Perl has references, but the "reference" in "pass-by-reference" is a
    different word, it means the parameters of the function are *aliases*
    of the arguments in the caller.
    Xavier Noria, Aug 28, 2008
    #12
    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. Alexander Malkis
    Replies:
    8
    Views:
    509
    Alexander Malkis
    Apr 14, 2004
  2. Alex
    Replies:
    2
    Views:
    1,206
  3. Roger23
    Replies:
    2
    Views:
    986
    Roger23
    Oct 12, 2006
  4. Replies:
    26
    Views:
    2,094
    Roland Pibinger
    Sep 1, 2006
  5. pereges
    Replies:
    7
    Views:
    425
    pereges
    Jun 1, 2008
Loading...

Share This Page