"Brian Schr?der" <ruby / brian-schroeder.de> schrieb im Newsbeitrag 
news:20041211115044.674cfc6a / black.wg...
> On Sat, 11 Dec 2004 19:42:23 +0900
> "Robert Klemme" <bob.news / gmx.net> wrote:
>
>>
>> "Brian Schr?der" <ruby / brian-schroeder.de> schrieb im Newsbeitrag
>> news:20041211105957.76dae75d / black.wg...
>> > On Sat, 11 Dec 2004 09:22:24 +0900
>> > Tim Hunter <cyclists / nc.rr.com> wrote:
>> >
>> >> Brian Schr?der wrote:
>> >>
>> >> > Interesting. But this won't work for instance variables that point 
>> >> > to
>> >> > arrays of deep_copy-able objects. Right?
>> >>
>> >> I knew you'd spot that :-) In a couple of cases I had to replace 
>> >> Arrays
>> >> with
>> >> something like this:
>> >>
>> >>         class Content < Array
>> >>             def deep_copy
>> >>                 copy = self.class.new
>> >>                 each do |c|
>> >>                     copy << case
>> >>                         when c.nil?
>> >>                             nil
>> >>                         when c.respond_to?(:deep_copy)
>> >>                             c.deep_copy
>> >>                         when c.respond_to?(:dup)
>> >>                             c.dup
>> >>                         else
>> >>                             c
>> >>                         end
>> >>                 end
>> >>                 return copy
>> >>             end
>> >>         end
>> >>
>> >> And, yes, I have to be careful not to use methods that return Array
>> >> objects.
>> >>
>> >
>> > Maybe it would make sense to extend the base classes Object, Array, 
>> > Hash
>> > with a
>> > deep-copy functionality. That would be something for the extensions
>> > project.
>>
>> IMHO not.  Reason beeing that the semantics of deep copy are class
>> dependend.  You might not want to copy all instances in an object graph 
>> for
>> deep copy and that might totally depend on the class at hand and / or 
>> (even
>> worse) application.  IMHO there is no real general solution to thid.  Of
>> course you could define a method in Object like
>>
>> def deep_copy
>>   Marshal.load( Marshal.dump( self ) )
>> end
>>
>> but you don't gain much that way.  And it won't even work in the general
>> case (consider Singletons etc.).
>>
>> > The problem here is, that we have object state that is not contained in
>> > "visible slots" i.e. instance variables. So this would be one case, 
>> > where
>> > the
>> > proposal for a
>>
>> There's a much more serious problem with the proposed implementation: it
>> does not cope with graphs of objects that contain cycles.  Do do that you
>> need to keep track of objects copied already.  Marshal does this - and 
>> it's
>> efficient.  If you want to do that yourself, you'll likely need a 
>> hash[old
>> oid -> copy] to manage this.  I doubt though that it's more efficient 
>> than
>> Marshal.
>>
>> I had to discover that there's more to deep copying than just traversing 
>> the
>> object graph and copying each instance in turn a while ago myself.  I 
>> help
>> you can benefit from my earlier errors... :-)
>>
>> Kind regards
>>
>>     robert
>>
>
> Ok, then back to the original question. If I don't want a general 
> solution, but
> want to deep-copy my special class, that contains some instance variables, 
> with
> some multi-dimensional arrays in it. What is a nice way to go about it?

This seems to work fairly good:

class Object
  def deep_copy(h = {})
    ident = self.id
    cpy = h[ident]

    unless cpy
      cpy = case self
        when String
          frozen? ? self : dup
        when Array
          map {|o| o.deep_copy(h)}
        when Hash
          # this looses some state (i.e. default etc.)
          inject({}) {|c, (k,v)| c[k.deep_copy(h)] = v.deep_copy(h); c}
        when Class, Module, Symbol, FalseClass, TrueClass, NilClass, Fixnum, 
Bignum
          self
        # probably more special cases like Struct
        else
          cpy = self.class.allocate

          instance_variables.each do |var|
            cpy.instance_variable_set(var, 
instance_variable_get(var).deep_copy(h))
          end

          cpy
      end

      cpy.freeze if frozen?
      h[ident] = cpy
    end

    cpy
  end
end

Kind regards

    robert