deepcopy via eval

O

OliverMarchand

Dear Ruby people,

I spent quite a while yesterday reviewing the discussions on deep
copying in ruby. An approach that I have not seen anywhere is to use a
combination of eval and inspect to deep copy an object. The essence of
the idea is:

obj_copy = eval(obj.inspect)

The standard approach is via Marshalling, but it doesn't really work:
e.g.

[[1,2,3]]*3

is a typical array, which does not contain three independent arrays,
but three pointers to the same array [1,2,3]. When Marshalling is
applied to copy this object the internal structure is preserved, which
may be a desirable feature or not. BTW: is there a good way to display
(a) that exact internal structure, (b) the contents of a Marshal
serialize string?

Now with the above eval method you get a real deep copy of the
123array, but the method is (a) slow and (b) does not work for objects
which do not return a string that can be used for instantiation of that
exact same object upon receiving inspect (which is the regular case).
But it works for simple combinations of strings, hashes, arrasy, floats
and ints, etc.

Now to make it more usable, I have combined the two in a very simple
module. One could also mix these into classes to make them accessible
via a method. The code below works for more things than the simple
Marshal version, but the behaviour is far from "under perfect control",
thus I hesitate to continue with this module. Are there comments and/or
better version and/or extensions out there?

---
module Deepcopy

def Deepcopy.bymarshal(obj)
return Marshal.load(Marshal.dump(obj))
end

def Deepcopy.byeval(obj)
if obj==nil
return nil
else
begin
e = eval(obj.inspect)
rescue Exception
end
e = Deepcopy.bymarshal(obj) if e == nil
return e
end
end

end
---

ciao,
Oliver

--
Oliver Marchand
Wunderlistr. 45
CH-8037 Zürich
Mobile: +41 78 8032102
Email (private): (e-mail address removed)
Email (work): (e-mail address removed)
 
O

OliverMarchand

I apologize for the multiple posts of this message. Google groups
assured me several times that due to server timeout the message was not
sent yet and the copies appeared hours after the last message was
finally sent "correctly".

oh, well,
Oliver
 
R

Robert Klemme

IMHO this is not a good idea for several reasons, the most serious
probably being:

- Marshal works for far more object types than your approach with
inspect and eval

- even in a deep copy you want the structure preserved, i.e. not
suddenly get two objects where there was just one on the input side

- eval might not be accessible (security constraints)

Kind regards

robert
 
O

OliverMarchand

Dear Robert,

I mostly agree with your criticism, thanks for the input.

Nevertheless I have pursuied the approach a little bit and added a
deepcopy method to the class Object, using the approach described
above. I only want to mention that hen one probably needs to reserve
another additional method, which returns the "instantiation string". I
consider that undesirable.

cheers,
Oliver
 
S

Stefano Taschini

Ufpasse!

Your method does not work for floats either, as #inspect dumps floats
with not nearly enough digits.

x = 1.9 - 0.9
#=> 1.0
x.inspect
#=> "1.0"
eval(x.inspect) == x
#=> false

The reason can be seen with 17 digits:

"%.17f" % x
#=> "0.99999999999999989"

Ciao,
Stefano

P.S.
In Python, the two functions repr() and str() are equivalent to Ruby's
#inspect and #to_s. For floats repr() includes enough digits to
reconstruct exactly the value of the floating-point number. This makes
life slightly easier for experts, but can be extremely confusing for
the casual programmer: one of the Python FAQs is about why 1.9-0.9
yields 0.99999999999999989.
 
O

OliverMarchand

Yes, you are right. repr is exactly the function that I added for that
pupose.

I see that the whole eval approach for deep copying is fairly useless.

Now I am wondering what the best solution to convert arrays like
x = [[1,2,3]]*3
into an equivalent structure, which changes only the first element upon

x[0] = ...whatever...


???
Oliver
 
R

Robert Klemme

OliverMarchand said:
Yes, you are right. repr is exactly the function that I added for that
pupose.

I see that the whole eval approach for deep copying is fairly useless.

Now I am wondering what the best solution to convert arrays like
x = [[1,2,3]]*3
into an equivalent structure, which changes only the first element upon

x[0] = ...whatever...

x.map {|i| i.dup}

robert
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Similar Threads

deepcopy questions 4
Can't deepcopy bytes-derived class 5
deepcopy alternative? 3
RUBY EVAL FUNCTION 7
deepcopy debugging 0
Fun about inspect 3
Ruby eval 5
Why I have to do a deepcopy to the argument? 0

Members online

Forum statistics

Threads
473,982
Messages
2,570,190
Members
46,736
Latest member
zacharyharris

Latest Threads

Top