Cloning object with array members

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

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)

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

···

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?

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

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

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

Alle 22:35, martedì 9 gennaio 2007, Shea Martin ha scritto:

   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

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

def clone
  rslt = BuildConfig.new

  self.instance_variables.each do |member|
    begin rslt.instance_variable_set( member, self.instance_variable_get(member).clone )
    rescue Exception: rslt.instance_variable_set(member, self.instance_variable_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