PLEASE HELP...dup is not working correctly in the following code

T

timr

#dup creates a copy of an object with a different object_id. As
follows:
a = [1,2,3] => [1, 2, 3]
b = a.dup => [1, 2, 3]
b << 1000 => [1, 2, 3, 1000]
a => [1, 2, 3]
b
=> [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# # # # # #"
# >> "# # ##### ##### # # ### # # ####### #"
# >> "# # # # # # # # # # # #"
# >> "# ### ### # # # # ##### # # # ##### #"
# >> "# # # # # # #"
# >> "#####################################"
 
S

Seebs

However, unlike the irb session, the following code shows that
modification of the dupped object is somehow changing the original.

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
 
T

timr

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.

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:
a = [['a', 'b'], ['c','d']] => [["a", "b"], ["c", "d"]]
a.each{|r| r.each{|letter| p letter.object_id}}
4442750
4442740
4442710
4442700
=> [["a", "b"], ["c", "d"]]
b = a.map{|r| r.each{|letter| letter.dup}} => [["a", "b"], ["c", "d"]]
b.each{|r| r.each{|letter| p letter.object_id}}
4442750
4442740
4442710
4442700

Still the same object_ids for all of the contents.
Tim
 
S

Seebs

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:
Hmm.
a = [['a', 'b'], ['c','d']] => [["a", "b"], ["c", "d"]]
a.each{|r| r.each{|letter| p letter.object_id}}
4442750
4442740
4442710
4442700
=> [["a", "b"], ["c", "d"]]

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
 
J

Josh Cheek

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

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

This is the difference

$ irb
a = [ 'w' , 'x' , 'y' ]
=> ["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.
 
P

Phillip Gawlowski

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:

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
 
T

timr

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:
Hmm.
a = [['a', 'b'], ['c','d']] => [["a", "b"], ["c", "d"]]
a.each{|r| r.each{|letter| p letter.object_id}}
4442750
4442740
4442710
4442700
=> [["a", "b"], ["c", "d"]]
b = a.map{|r| r.each{|letter| letter.dup}}

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

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

Rimantas Liubertas

#dup creates a copy of an object with a different object_id. As
follows:
a =3D ["bar", "baz"] =3D> ["bar", "baz"]
b =3D a.dup =3D> ["bar", "baz"]
c =3D Marshal.load(Marshal.dump(a)) =3D> ["bar", "baz"]
b[0] << "s" =3D> "bars"
b =3D> ["bars", "baz"]
a =3D> ["bars", "baz"]
c =3D> ["bar", "baz"]
a.object_id =3D> 2157344160
b.object_id =3D> 2157338800
c.object_id =3D> 2157328340
a[0].object_id =3D> 2157344140
b[0].object_id =3D> 2157344140
c[0].object_id
=3D> 2157328320

Regards,
Rimantas
 
S

Seebs

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

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

-s
 
T

timr

#dup creates a copy of an object with a different object_id. As
follows:
<…>




a = ["bar", "baz"] => ["bar", "baz"]
b = a.dup => ["bar", "baz"]
c = Marshal.load(Marshal.dump(a)) => ["bar", "baz"]
b[0] << "s" => "bars"
b => ["bars", "baz"]
a => ["bars", "baz"]
c => ["bar", "baz"]
a.object_id => 2157344160
b.object_id => 2157338800
c.object_id => 2157328340
a[0].object_id => 2157344140
b[0].object_id => 2157344140
c[0].object_id

=> 2157328320

Regards,
Rimantas
--http://rimantas.com

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.
 

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

Members online

No members online now.

Forum statistics

Threads
473,763
Messages
2,569,562
Members
45,038
Latest member
OrderProperKetocapsules

Latest Threads

Top