Hash default -- what's going on here?

P

Pete Goodeve

I've hit what is either an egregious bug in ruby (1.8.4) or some subtlety
that I haven't grasped. [Most likely the latter... :)-)]

Briefly I had two arrays of symbol strings of which I wanted to find
which symbols occurred in both arrays, so I thought I'd use a hash with
the key being the symbol and the value a two element array that would
be used to flag which set(s) the symbol was found in. The following
is a simplified (and 'instrumented') version of what I was trying to
do:

listA=["line1","line2","line3"]
listB=["line5","line3","line4"]
table={}
table.default=[0,0]
print "table default originally: ", table.default, "\n"

listA.each {|a| t=table[a]; print a," -- undef value: ",t;
t[0]=1; print " new value: ",t,"\n";
table[a]=t}
print "table default now: ", table.default, "\n"

listB.each {|a| t=table[a]; print a," -- undef value: ",t;
t[1]=1; print " new value: ",t,"\n";
table[a]=t}

print "table default now: ", table.default, "\ntable contents:\n"
table.each {|k,v| print k, ": ", v[0], v[1], "\n"}


Now, in the 'each' statements, the variable 'a' should (except in one case)
not be in the hash yet, so I would expect the original provided default to
be returned. However, here's the printout I get:

table default now: 00
line1 -- undef value: 00 new value: 10
line2 -- undef value: 10 new value: 10
line3 -- undef value: 10 new value: 10
table default now: 10
line5 -- undef value: 10 new value: 11
line3 -- undef value: 11 new value: 11
line4 -- undef value: 11 new value: 11
table default now: 11
table contents:
line1: 11
line2: 11
line3: 11
line4: 11
line5: 11


Dunhh? Why is the default value getting changed? I can't see that
I'm resetting it anywhere. If I do the same sort of manipulations
outside of a block (one at a time in irb for instance) things happen
as I would expect -- the default remains as set, and is returned for
any undefined key.

Illumination appreciated.
-- Pete --
 
D

Daniel Baird

------=_Part_6549_12041498.1142407055374
Content-Type: text/plain; charset=ISO-8859-1
Content-Transfer-Encoding: quoted-printable
Content-Disposition: inline

I'm guessing it's got something to do with your assignment

table[a]=3Dt

the array that is the hash's default can be assigned to. I think that's
what you're doing. If i did some testing I might be more sure :)

;Daniel

I've hit what is either an egregious bug in ruby (1.8.4) or some subtlety
that I haven't grasped. [Most likely the latter... :)-)]

Briefly I had two arrays of symbol strings of which I wanted to find
which symbols occurred in both arrays, so I thought I'd use a hash with
the key being the symbol and the value a two element array that would
be used to flag which set(s) the symbol was found in. The following
is a simplified (and 'instrumented') version of what I was trying to
do:

listA=3D["line1","line2","line3"]
listB=3D["line5","line3","line4"]
table=3D{}
table.default=3D[0,0]
print "table default originally: ", table.default, "\n"

listA.each {|a| t=3Dtable[a]; print a," -- undef value: ",t;
t[0]=3D1; print " new value: ",t,"\n";
table[a]=3Dt}
print "table default now: ", table.default, "\n"

listB.each {|a| t=3Dtable[a]; print a," -- undef value: ",t;
t[1]=3D1; print " new value: ",t,"\n";
table[a]=3Dt}

print "table default now: ", table.default, "\ntable contents:\n"
table.each {|k,v| print k, ": ", v[0], v[1], "\n"}


Now, in the 'each' statements, the variable 'a' should (except in one
case)
not be in the hash yet, so I would expect the original provided default t= o
be returned. However, here's the printout I get:

table default now: 00
line1 -- undef value: 00 new value: 10
line2 -- undef value: 10 new value: 10
line3 -- undef value: 10 new value: 10
table default now: 10
line5 -- undef value: 10 new value: 11
line3 -- undef value: 11 new value: 11
line4 -- undef value: 11 new value: 11
table default now: 11
table contents:
line1: 11
line2: 11
line3: 11
line4: 11
line5: 11


Dunhh? Why is the default value getting changed? I can't see that
I'm resetting it anywhere. If I do the same sort of manipulations
outside of a block (one at a time in irb for instance) things happen
as I would expect -- the default remains as set, and is returned for
any undefined key.

Illumination appreciated.
-- Pete --


--
Daniel Baird
http://danielbaird.com (TiddlyW;nks! :: Whiteboard Koala :: Blog :: Things
That Suck)
[[My webhost uptime is ~ 92%.. if no answer pls call again later!]]

------=_Part_6549_12041498.1142407055374--
 
G

Gerardo Santana Gómez Garrido

2006/3/15 said:
I've hit what is either an egregious bug in ruby (1.8.4) or some subtlety
that I haven't grasped. [Most likely the latter... :)-)]

Briefly I had two arrays of symbol strings of which I wanted to find
which symbols occurred in both arrays, so I thought I'd use a hash with
the key being the symbol and the value a two element array that would
be used to flag which set(s) the symbol was found in. The following
is a simplified (and 'instrumented') version of what I was trying to
do:

listA=3D["line1","line2","line3"]
listB=3D["line5","line3","line4"]
table=3D{}

Here's the problem:
table.default=3D[0,0]


What you are looking for is:

table =3D Hash.new { |h, k| h[k] =3D [0, 0] }

otherwise you are always pointing to _the same_ object
 
P

Pete Goodeve

------=_Part_6549_12041498.1142407055374
Content-Type: text/plain; charset=ISO-8859-1
Content-Transfer-Encoding: quoted-printable
Content-Disposition: inline

I'm guessing it's got something to do with your assignment

table[a]=3Dt

the array that is the hash's default can be assigned to. I think that's
what you're doing. If i did some testing I might be more sure :)

Heh -- you can tell I'm new to ruby... :)-))

An hour after I posted I suddenly realized what is going on.
When you assign an array, the array is *not* copied! the assigned-to
variable just gets a pointer to the other array.

So when I did "t=table[a]" I was setting t to a pointer to the default!
When I changed t[n], of course the default was actually what got changed...

Sigh. Hardly a novel gotcha, but I fell right into it.

Not sure of the best way to do what I want -- maybe just use 'has_key?'
and then do the right thing.
-- Pete --
 
R

Robert Klemme

Pete Goodeve said:
Not sure of the best way to do what I want -- maybe just use 'has_key?'
and then do the right thing.

The question is what exactly are you trying to do?

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

Members online

No members online now.

Forum statistics

Threads
473,996
Messages
2,570,238
Members
46,826
Latest member
robinsontor

Latest Threads

Top