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 M. wrote:

‘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

On Jan 5, 2007, at 4:40 PM, Shea M. 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 W.

Shea M. wrote:

‘cleanest’ way to do this?
end
rslt.arr1 = @arr1.clone

~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

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

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

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

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

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