From: Stefano Crocco Date: 2008-03-22T22:37:46+09:00 Subject: Re: Cloning arrays On Saturday 22 March 2008, Len Lawrence wrote: > Another newbie question. I have started recoding some of my homegrown > Tcl/Tk applications in Ruby/rubytk and have hit an unexpected snag when > attempting to clone arrays. The following code is snipped verbatim from > the program: > > # Calculate coordinates for the Postscript page canvas and the > # label icons canvas in the setupInkjet window. > > def User.calcxy ( disposition, f ) > > xygrid = { } > > # Split the disposition string into x and y counts. > xy = Typefaces::Disposition[disposition] > # Obtain the label dimensions. > labelsize = Typefaces::Labelsizes[disposition] > # Populate the coordinate data structure. > xygrid['x'] = nx = xy[0] > xygrid['y'] = ny = xy[1] > xygrid['dx'] = dx = labelsize[0] * f > xygrid['dy'] = dy = labelsize[1] * f > # Obtain an array of 4 element arrays from the Typefaces module. > # These represent opposing corners of the address spaces on the > # Postscript canvas. > xygrid['page'] = Typefaces.labelgrid( disposition ) > puts 'page coordinates' > puts xygrid['page'][0] > puts '______________' > # Copy the page label coordinates. > e = xygrid['page'].clone > # Shrink the label icon coordinates. > e.each { |z| z.collect! { |d| d *= f } } > # Shrink the icons further to make separations visible. > e.each { |z| z[2] -= 2.0; z[3] -= 2.0 } > # Save the label icon coordinates. > xygrid['boxes'] = e > puts 'page again' > puts xygrid['page'][0] > puts 'boxes' > puts e[0] > puts '==============' > xygrid['max'] = nx * ny > return xygrid > > end > > The diagnostics at 'page again' and 'boxes' show that the copy of the > original array has been successfully processed but the original array now > reflects the same values. The 'page' and 'boxes' arrays are to be used in > different circumstances. > > What am I missing? It is bound to be something simple or something > fundamental. I tried Object#dup also - same result. > > Len I don't know rubytk, so I couldn't completely follow your code, but I think your problem is related to the fact that clone and dup both perform shallow copies of the array. This means that, while the array itself is duplicated, its contents are not. For example, running this code a = %w[a b c] b = a.dup puts "The ID of a is #{a.object_id}" puts "The ID of b is #{b.object_id}" puts "a is the same object as b? #{a.equal? b}" puts "The ID of the contents of a: #{a.map{|i| i.object_id}.join(', ')}" puts "The ID of the contents of b: #{b.map{|i| i.object_id}.join(', ')}" a.each_index do |i| puts "a[#{i}] is the same object as b[#{i}]? #{a[i].equal? b[i]}" end produces: The ID of a is -605837488 The ID of b is -605837528 a is the same object as b? false The ID of the contents of a: -605837498, -605837508, -605837518 The ID of the contents of b: -605837498, -605837508, -605837518 a[0] is the same object as b[0]? true a[1] is the same object as b[1]? true a[2] is the same object as b[2]? true This means that while a and b are not the same object (and so, you can modify one, for example adding or removing an item, leaving the other as is), they contain the same objects. So, calling 'destructive' methods (that is, methods which change the receiver) on the elements of one of the array will also change the corresponding element of the other array (because they contain the same object). To achieve what you want, you need deep copy. As someone on this list recently pointed out, you can usually achieve this using marshal: b = Marshal.load(Marshal.dump(a)) The problem is that with rubytk you're dealing with a C extension, and with objects created in C, which may, or may not, work correctly with marshal. If you find out they don't, you can try to manually deep-duplicate the array. In the simple case of my example above, you can do: b = a.map{|i| a.dup} If your array is a nested array, things become more complicated, as you'd need nested loops. You also need to keep in mind that object created from C extensions may not be clonable. In this case, there's nothing to do. I hope this helps Stefano