From: Robert Klemme Date: 2004-12-12T20:12:29+09:00 Subject: Re: How to make a deep copy of an object (Searching for Idiom) [Object State] "Brian Schr�der" schrieb im Newsbeitrag news:20041211115044.674cfc6a@black.wg... > On Sat, 11 Dec 2004 19:42:23 +0900 > "Robert Klemme" wrote: > >> >> "Brian Schr�der" schrieb im Newsbeitrag >> news:20041211105957.76dae75d@black.wg... >> > On Sat, 11 Dec 2004 09:22:24 +0900 >> > Tim Hunter 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