cloning object with array members

Discussion in 'Ruby' started by Shea Martin, Jan 5, 2007.

  1. Shea Martin

    Shea Martin Guest

    Consider a simple class like this:
    class MyClass
    def initialize
    @var1 = ''
    @arr1 = []
    end
    end


    Now if I have an instance of MyClass, and want to clone it, what is the
    'cleanest' way to do this?

    a = MyClass.new
    b = a.clone

    This method leaves 'b.arr1' pointing to the same data as a.arr1.

    Hmmm... I'll try overriding clone, and manually deal with the arrays:

    def clone
    rslt = super.clone
    end

    This blows the stack, I believe because all methods are inherited virtually.

    Ok, so now I have to manually copy everything in clone, whatever, I am
    getting sick of this:

    def clone
    rslt = MyClass.new
    rslt.var1 = @var1
    rslt.arr1 = @arr1.clone
    rslt
    end

    Turns out this does not compute either, as there is no method var1= or
    arr1= ! I want to keep var1 private, so can't go this route either.


    One more attempt:
    def clone
    rslt = MyClass.new

    self.instance_variables.each do |member|
    rslt.instance_variable_set( member,
    self.instance_variable_get(member).clone )
    end
    end

    This fails as the methos do not seem to copy over.

    There has to be a simple way to do something like this? What is common
    recipe for something like this? Should this really be that hard in ruby?

    ~S
     
    Shea Martin, Jan 5, 2007
    #1
    1. Advertising

  2. Shea Martin

    Guest

    Hi --

    On Sat, 6 Jan 2007, Shea Martin wrote:

    > Consider a simple class like this:
    > class MyClass
    > def initialize
    > @var1 = ''
    > @arr1 = []
    > end
    > end
    >
    >
    > Now if I have an instance of MyClass, and want to clone it, what is the
    > 'cleanest' way to do this?
    >
    > a = MyClass.new
    > b = a.clone
    >
    > This method leaves 'b.arr1' pointing to the same data as a.arr1.


    I don't see an a.arr1 method. Did you mean to create some accessors
    methods?

    The most common idiom I've seen for deep copying is:

    b = Marshal.load(Marshal.dump(a))


    David

    --
    Q. What is THE Ruby book for Rails developers?
    A. RUBY FOR RAILS by David A. Black (http://www.manning.com/black)
    (See what readers are saying! http://www.rubypal.com/r4rrevs.pdf)
    Q. Where can I get Ruby/Rails on-site training, consulting, coaching?
    A. Ruby Power and Light, LLC (http://www.rubypal.com)
     
    , Jan 5, 2007
    #2
    1. Advertising

  3. Shea Martin

    Guest

    On Jan 5, 2007, at 4:40 PM, Shea Martin wrote:
    > Hmmm... I'll try overriding clone, and manually deal with the arrays:
    >
    > There has to be a simple way to do something like this? What is
    > common recipe for something like this? Should this really be that
    > hard in ruby?


    Warning: I didn't test these...

    def clone
    rslt = super # Use the default #clone instead of Myclass.new

    instance_variables.each do |member|
    rslt.instance_variable_set( member, instance_variable_get
    (member).clone )
    end

    rslt
    end

    For your particular case you could avoid the loop of course:

    def clone
    rslt = super
    rslt.instance_variable_set "@var1", @var1.clone
    rslt.instance_variable_set "@arr1", @arr1.clone
    rslt
    end



    Gary Wright
     
    , Jan 5, 2007
    #3
  4. Shea Martin

    Olivier Guest

    > Hmmm... I'll try overriding clone, and manually deal with the arrays:
    >
    > def clone
    > rslt = super.clone
    > end
    >
    > This blows the stack, I believe because all methods are inherited
    > virtually.


    The method which must be overriden is initialize_copy

    def initialize_copy(from)
    @arr1 = from.instance_eval('@arr1').clone
    end

    --
    Olivier Renaud
     
    Olivier, Jan 5, 2007
    #4
  5. Shea Martin

    Shea Martin Guest

    Shea Martin wrote:
    > Consider a simple class like this:
    > class MyClass
    > def initialize
    > @var1 = ''
    > @arr1 = []
    > end
    > end
    >
    >
    > Now if I have an instance of MyClass, and want to clone it, what is the
    > 'cleanest' way to do this?
    >
    > a = MyClass.new
    > b = a.clone
    >
    > This method leaves 'b.arr1' pointing to the same data as a.arr1.
    >
    > Hmmm... I'll try overriding clone, and manually deal with the arrays:
    >
    > def clone
    > rslt = super.clone
    > end
    >
    > This blows the stack, I believe because all methods are inherited
    > virtually.
    >
    > Ok, so now I have to manually copy everything in clone, whatever, I am
    > getting sick of this:
    >
    > def clone
    > rslt = MyClass.new
    > rslt.var1 = @var1
    > rslt.arr1 = @arr1.clone
    > rslt
    > end
    >
    > Turns out this does not compute either, as there is no method var1= or
    > arr1= ! I want to keep var1 private, so can't go this route either.
    >
    >
    > One more attempt:
    > def clone
    > rslt = MyClass.new
    >
    > self.instance_variables.each do |member|
    > rslt.instance_variable_set( member,
    > self.instance_variable_get(member).clone )
    > end
    > end
    >
    > This fails as the methos do not seem to copy over.
    >
    > There has to be a simple way to do something like this? What is common
    > recipe for something like this? Should this really be that hard in ruby?
    >
    > ~S



    Thanks for all the help. My last attempt at clone turns out to be fine,
    I just forgot to return 'rslt'. That fixed everything.

    ~S
     
    Shea Martin, Jan 6, 2007
    #5
  6. Shea Martin

    Pit Capitain Guest

    Shea, you found your answer in the meantime, but I wanted to show you
    what was wrong with your other attempts:

    >> a = MyClass.new
    >> b = a.clone
    >>
    >> This method leaves 'b.arr1' pointing to the same data as a.arr1.


    Yes, the default #dup and #clone create a shallow copy.

    >> def clone
    >> rslt = super.clone
    >> end
    >>
    >> This blows the stack, I believe because all methods are inherited
    >> virtually.


    "super" isn't the same as in Java. super calls the same method of the
    superclass, so what you are doing here is:

    temp = super
    rslt = temp.clone

    Since temp is an instance of MyClass, you're calling clone again, which
    gives you and endless recursion. The correct way would have been:

    def clone
    super
    end

    But this obviously doen't help you.

    >> def clone
    >> rslt = MyClass.new
    >> rslt.var1 = @var1
    >> rslt.arr1 = @arr1.clone
    >> rslt
    >> end
    >>
    >> Turns out this does not compute either, as there is no method var1= or
    >> arr1= ! I want to keep var1 private, so can't go this route either.


    The solution to this problem was:

    >> def clone
    >> rslt = MyClass.new
    >>
    >> self.instance_variables.each do |member|
    >> rslt.instance_variable_set( member,
    >> self.instance_variable_get(member).clone )
    >> end
    >> end
    >>
    >> This fails as the methos do not seem to copy over.


    And here, as you've noted yourself, you just forgot to return your new
    instance.

    Regards,
    Pit
     
    Pit Capitain, Jan 6, 2007
    #6
  7. Shea Martin

    Shea Martin Guest

    def clone
    rslt = BuildConfig.new

    self.instance_variables.each do |member|
    rslt.instance_variable_set( member,
    self.instance_variable_get(member).clone )
    end

    return rslt
    end

    This clone method bombs if any of the instance variables are FixNums,
    Floats, boolean, etc. Is there a way in my loop to know if an instance
    variable supports 'clone'?

    ~S
     
    Shea Martin, Jan 9, 2007
    #7
  8. Alle 22:35, marted=EC 9 gennaio 2007, Shea Martin ha scritto:
    > def clone
    > rslt =3D BuildConfig.new
    >
    > self.instance_variables.each do |member|
    > rslt.instance_variable_set( member,
    > self.instance_variable_get(member).clone )
    > end
    >
    > return rslt
    > end
    >
    > This clone method bombs if any of the instance variables are FixNums,
    > Floats, boolean, etc. Is there a way in my loop to know if an instance
    > variable supports 'clone'?
    >
    > ~S


    I don't know whether there is a way to know in advance if a variable suppor=
    ts=20
    clone, so I'd say: try it. If it works, good. If it doesn't, it'll raise an=
    =20
    exception, which you can rescue:

    def clone
    rslt =3D BuildConfig.new

    self.instance_variables.each do |member|
    begin rslt.instance_variable_set( member, self.instance_variable_get(me=
    mber).clone )
    rescue Exception: rslt.instance_variable_set(member, self.instance_vari=
    able_get(member))
    end
    end

    return rslt
    end

    I'm not an expert, so there may be better ways to do this I don't know, but=
    this should work.

    Stefano
     
    Stefano Crocco, Jan 9, 2007
    #8
    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. Marco Schmidt

    Re: Cloning String object

    Marco Schmidt, Aug 23, 2003, in forum: Java
    Replies:
    0
    Views:
    384
    Marco Schmidt
    Aug 23, 2003
  2. Will Clark

    Re: Cloning String object

    Will Clark, Aug 23, 2003, in forum: Java
    Replies:
    0
    Views:
    6,070
    Will Clark
    Aug 23, 2003
  3. Elhanan
    Replies:
    0
    Views:
    301
    Elhanan
    Dec 27, 2006
  4. Paul N
    Replies:
    5
    Views:
    378
    Robert Fendt
    Feb 1, 2010
  5. Eric Mahurin

    Cloning into an object

    Eric Mahurin, Apr 30, 2005, in forum: Ruby
    Replies:
    3
    Views:
    100
    Charles Steinman
    May 1, 2005
Loading...

Share This Page