Discussion in 'Ruby' started by timr, Dec 29, 2009.

1. ### timrGuest

#dup creates a copy of an object with a different object_id. As
follows:
=> [1, 2, 3, 1000]

Changing b does't change a because b is a copy of a with a different
object_id.

However, unlike the irb session, the following code shows that
modification of the dupped object is somehow changing the original.
WTF? (you may want to change the font to courier to get the maze to
line up correctly). This behavior is so strange. I can see no
significant differences between the usage in the irb session and in
this code, yet the behavior is very different. Please explain if you
have any insights.
Thanks,
Tim

@maze1 = %{#####################################
# # # #A # # #
# # # # # # ####### # ### # ####### #
# # # # # # # # #
# ##### # ################# # #######
# # # # # # # # #
##### ##### ### ### # ### # # # # # #
# # # # # # B# # # # # #
# # ##### ##### # # ### # # ####### #
# # # # # # # # # # # #
# ### ### # # # # ##### # # # ##### #
# # # # # # #
#####################################}
class Maze
attr_accessor :maze

#initialize creates a @maze array with each row a sub array
def initialize(maze_name)
@maze = maze_name.split(/\n/).collect{|row| row.split(//)}
@maze_dup = @maze.dup
end
def write_(r, c)
@maze_dup[r][c] = "a"
end
def report
puts "@maze:"
@maze.each{|r| p r.join}
puts "@maze_dup:"
@maze_dup.each{|r| p r.join}
end
end

maze1 = Maze.new(@maze1)
maze1.report
maze1.write_(0,0)
maze1.report
maze1.write_(1,1)
maze1.report

# >> @maze:
# >> "#####################################"
# >> "# # # #A # # #"
# >> "# # # # # # ####### # ### # ####### #"
# >> "# # # # # # # # #"
# >> "# ##### # ################# # #######"
# >> "# # # # # # # # #"
# >> "##### ##### ### ### # ### # # # # # #"
# >> "# # # # # # B# # # # # #"
# >> "# # ##### ##### # # ### # # ####### #"
# >> "# # # # # # # # # # # #"
# >> "# ### ### # # # # ##### # # # ##### #"
# >> "# # # # # # #"
# >> "#####################################"
# >> @maze_dup:
# >> "#####################################"
# >> "# # # #A # # #"
# >> "# # # # # # ####### # ### # ####### #"
# >> "# # # # # # # # #"
# >> "# ##### # ################# # #######"
# >> "# # # # # # # # #"
# >> "##### ##### ### ### # ### # # # # # #"
# >> "# # # # # # B# # # # # #"
# >> "# # ##### ##### # # ### # # ####### #"
# >> "# # # # # # # # # # # #"
# >> "# ### ### # # # # ##### # # # ##### #"
# >> "# # # # # # #"
# >> "#####################################"
# >> @maze:
# >> "a####################################"
# >> "# # # #A # # #"
# >> "# # # # # # ####### # ### # ####### #"
# >> "# # # # # # # # #"
# >> "# ##### # ################# # #######"
# >> "# # # # # # # # #"
# >> "##### ##### ### ### # ### # # # # # #"
# >> "# # # # # # B# # # # # #"
# >> "# # ##### ##### # # ### # # ####### #"
# >> "# # # # # # # # # # # #"
# >> "# ### ### # # # # ##### # # # ##### #"
# >> "# # # # # # #"
# >> "#####################################"
# >> @maze_dup:
# >> "a####################################"
# >> "# # # #A # # #"
# >> "# # # # # # ####### # ### # ####### #"
# >> "# # # # # # # # #"
# >> "# ##### # ################# # #######"
# >> "# # # # # # # # #"
# >> "##### ##### ### ### # ### # # # # # #"
# >> "# # # # # # B# # # # # #"
# >> "# # ##### ##### # # ### # # ####### #"
# >> "# # # # # # # # # # # #"
# >> "# ### ### # # # # ##### # # # ##### #"
# >> "# # # # # # #"
# >> "#####################################"
# >> @maze:
# >> "a####################################"
# >> "#a# # #A # # #"
# >> "# # # # # # ####### # ### # ####### #"
# >> "# # # # # # # # #"
# >> "# ##### # ################# # #######"
# >> "# # # # # # # # #"
# >> "##### ##### ### ### # ### # # # # # #"
# >> "# # # # # # B# # # # # #"
# >> "# # ##### ##### # # ### # # ####### #"
# >> "# # # # # # # # # # # #"
# >> "# ### ### # # # # ##### # # # ##### #"
# >> "# # # # # # #"
# >> "#####################################"
# >> @maze_dup:
# >> "a####################################"
# >> "#a# # #A # # #"
# >> "# # # # # # ####### # ### # ####### #"
# >> "# # # # # # # # #"
# >> "# ##### # ################# # #######"
# >> "# # # # # # # # #"
# >> "##### ##### ### ### # ### # # # # # #"
# >> "# # # # # # B# # # # # #"
# >> "# # ##### ##### # # ### # # ####### #"
# >> "# # # # # # # # # # # #"
# >> "# ### ### # # # # ##### # # # ##### #"
# >> "# # # # # # #"
# >> "#####################################"

timr, Dec 29, 2009

2. ### SeebsGuest

No, it isn't.

You have two separate arrays.

However, each of those arrays contains the same strings -- the first string
of each array is the same object.

You want a deep copy (where you duplicate all the objects in the array,
not just the arrays). Something like:

@maze_dup = @maze.map { |x| x.dup }

.... I think.

-s

Seebs, Dec 29, 2009

3. ### timrGuest

You are right about the strings having the same object_ids in each
object, which explains why the modification on the dupped object
affects the original strings. That helps. But the suggested strategy
doesn't solve the problem:
4442750
4442740
4442710
4442700
=> [["a", "b"], ["c", "d"]]
4442750
4442740
4442710
4442700

Still the same object_ids for all of the contents.
Tim

timr, Dec 29, 2009
4. ### SeebsGuest

This doesn't seem right. The inner loop (which is the one I think
we care about) is using each, not map. So we never actually return
the array consisting of all the duplicated letters.

If you have an array of arrays, I think you need to use map at both
levels.

irb(main):001:0> a = [['a', 'b'], ['c','d']]
=> [["a", "b"], ["c", "d"]]
irb(main):002:0> b = a.map {|r| r.map{|l| l.dup}}
=> [["a", "b"], ["c", "d"]]
irb(main):003:0> a.each{|r| r.each{|letter| p letter.object_id}}
2152912244
2152912216
2152912132
2152912104
=> [["a", "b"], ["c", "d"]]
irb(main):004:0> b.each{|r| r.each{|letter| p letter.object_id}}
2152886456
2152886316
2152886232
2152886176
=> [["a", "b"], ["c", "d"]]

What's throwing you off here is that .each returns the array object
it started with.

So "r.each {...}" always returns r, unmodified.

-s

Seebs, Dec 29, 2009
5. ### Josh CheekGuest

[Note: parts of this message were removed to make it a legal post.]

This is the difference

\$ irb
=> ["w", "x", "y"]
=> ["w", "x", "y"]
=> ["w", "x", "y", "z"]
=> ["w", "x", "y"]
=> "wx"
=> ["wx", "x", "y", "z"]
=> ["wx", "x", "y"]

a and b are different arrays, but they each contain the same objects.

I think the only way to make a deep copy is to explicitly dup everything in
the array also, or to use marshall. I'm not sure why there isn't a deep copy
method.

Josh Cheek, Dec 29, 2009
6. ### Phillip GawlowskiGuest

irb(main):001:0> array = ["string"]
=> ["string"]
irb(main):002:0> array_dup = []
=> []
irb(main):003:0> array_dup[0] = array[0].clone
=> "string"
irb(main):004:0> array_dup[0].object_id
=> 30816948
irb(main):005:0> array[0].object_id
=> 30838824

Phillip Gawlowski, Dec 29, 2009
7. ### timrGuest

I was just going to post that correction after I realized that
mistake. Thanks for the help.

timr, Dec 29, 2009
8. ### Rimantas LiubertasGuest

#dup creates a copy of an object with a different object_id. As
=3D> 2157328320

Regards,
Rimantas

Rimantas Liubertas, Dec 29, 2009
9. ### SeebsGuest

Thanks for asking the question with enough detail, and illustrations,
that it was *possible* to help.

-s

Seebs, Dec 29, 2009
10. ### timrGuest

This is in effect the deep copy of a complex array containing strings
that we were pining for. I think it is easier to do it this ways than
to map through all of the nested arrays. Thanks for the irb demo of
its use.

timr, Dec 29, 2009