2009/2/28 AD60 <andredeen / gmail.com>:
> Hi,
>
> I am puzzled by the workings a the following program. I placed the
> output after the corresponding lines. To me it lookes like a sort of
> bug in the workings of de p method. Can anybody set me straight?

There's no bug involved.

> h = Hash.new([])

Here you create Hash with an empty array as
default value. For every lookup of a key that is
not in the hash, this single array instance is
returned, but not automatically stored in the hash.
Again: It's a single object, not a new one on every
lookup.

> h[0] = [1,2,3]

Here you associate 0 with an array. The hash default
value is not involved.

> p h                      #> {0=>[1, 2, 3]}
> h[0] += [4]
> p h                      #> {0=>[1, 2, 3, 4]}

This is a syntactic shortcut for "h[0] = h[0] + [4]".
It looks up the value of 0 in the hash, which is
an array. array + array creates a new array,
which overwrites the previous value of h[0].

> h[1] += [10,11]
> p h                      #> {0=>[1, 2, 3, 4], 1=>[10, 11]}

Again, this is a shortcut for "h[1] = h[1] + [10, 11]".
The hash doesn't have a value for 1 yet, so
the default value ([]) is returned. [] + [10, 11] is
the new array [10, 11] which is then assigned
to h[1].

> h[1] << 12
> p h                     #> {0=>[1, 2, 3, 4], 1=>[10, 11, 12]}

Look up the existing array stored in h[1] and append
12. No new array created.

> h[2] << 20
> p h                     #> {0=>[1, 2, 3, 4], 1=>[10, 11, 12]  ?

The hash doesn't have a value for 2, so the default
value, [], is returned. The default value is destructively
modified by appending 20. _No new hash entry is
created (there is no assignment!)._

Future lookups of non-existent keys will return
the modified default value!

> p h[2]                 #> [20]  ???

The hash still doesn't have an entry for 2, so
the default value is returned, which by now is
our modified array.

> h[2] << 21
> p h                     #> {0=>[1, 2, 3, 4], 1=>[10, 11, 12]} ?

Still no entry for 2, default value is returned and modified
by appending 21.

> h[2] += [22]
> p h                     #> {0=>[1, 2, 3, 4], 1=>[10, 11, 12], 2=>[20, 21, 22]}  ????????

Again, this is equivalent to "h[2] = h[2] + [22]".
Still no entry for h[2], so the default value is returned,
which by now is [20, 21]. Then the two arrays
[20, 21] and [22] are concatenated, returning the
new array [20, 21, 22] which is then finally associated
with 2 in the hash (see: here is an assignment!).

What you probably want to achieve can be
done using Hash's block constructor:

  h = Hash.new { |hash, key| hash[key] = [] }

This automatically stores a new empty array
on every lookup of a non-existent key.

HTH,
  Stefan