deepcopy via eval

Discussion in 'Ruby' started by OliverMarchand, Apr 11, 2006.

  1. 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):
    Email (work):
     
    OliverMarchand, Apr 11, 2006
    #1
    1. Advertising

  2. 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
     
    OliverMarchand, Apr 11, 2006
    #2
    1. Advertising

  3. 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
     
    Robert Klemme, Apr 11, 2006
    #3
  4. 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
     
    OliverMarchand, Apr 12, 2006
    #4
  5. 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.
     
    Stefano Taschini, Apr 12, 2006
    #5
  6. 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
     
    OliverMarchand, Apr 12, 2006
    #6
  7. OliverMarchand wrote:
    > 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
     
    Robert Klemme, Apr 12, 2006
    #7
    1. Advertising

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

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. Eric Newton
    Replies:
    3
    Views:
    9,495
    Brock Allen
    Apr 4, 2005
  2. DataBinder.Eval and Eval.

    , Jun 16, 2006, in forum: ASP .Net
    Replies:
    1
    Views:
    563
    Karl Seguin [MVP]
    Jun 16, 2006
  3. Alex van der Spek

    eval('07') works, eval('08') fails, why?

    Alex van der Spek, Jan 8, 2009, in forum: Python
    Replies:
    6
    Views:
    1,494
    Bruno Desthuilliers
    Jan 8, 2009
  4. OliverMarchand

    deepcopy via eval

    OliverMarchand, Apr 11, 2006, in forum: Ruby
    Replies:
    0
    Views:
    108
    OliverMarchand
    Apr 11, 2006
  5. Liang Wang
    Replies:
    8
    Views:
    141
    Ben Morrow
    Feb 2, 2008
Loading...

Share This Page