Hash default -- what's going on here?

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 --

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

                        table[a]=t

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 :slight_smile:

;Daniel

···

On 15/03/06, Pete Goodeve <pete@jwgibbs.cchem.berkeley.edu> wrote:

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 --

--
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!]]

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={}

Here's the problem:

table.default=[0,0]

What you are looking for is:

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

otherwise you are always pointing to _the same_ object

···

2006/3/15, Pete Goodeve <pete@jwgibbs.cchem.berkeley.edu>:

In article <ca19f32e0603142317y1b97a5c9k@mail.gmail.com>,

------=_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 :slight_smile:

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 --

···

Daniel Baird <danielbaird@gmail.com> wrote:

"Pete Goodeve" <pete@jwgibbs.cchem.berkeley.edu> wrote in message news:dv8lhf$18m$1@jwgibbs.CChem.Berkeley.EDU...

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