Forum: Ruby Isn't hash default value behavior just a little bizarre???

Announcement (2017-05-07): www.ruby-forum.com is now read-only since I unfortunately do not have the time to support and maintain the forum any more. Please see rubyonrails.org/community and ruby-lang.org/en/community for other Rails- und Ruby-related community platforms.
B92e77600ddf31da8b72c2637431d2cf?d=identicon&s=25 Farhad Farzaneh (clipper)
on 2007-07-04 09:19
Try this:

>> x = Hash.new([])
=> {}
>> x[:test]
=> []
>> x[:test] << 'this'
=> ["this"]
>> x[:bar]
=> ["this"]

Huh?  It seems that when I index a hash with a new key, it returns the
Hash's default value object, not a copy of it.  So if you modify that,
it modifies the default value, which then effects the default value for
every other new key.

I don't know, I wouldn't call this the "principle of least surprise"...
I was plenty surprised!
6087a044557d6b59ab52e7dd20f94da8?d=identicon&s=25 Peña, Botp (Guest)
on 2007-07-04 09:37
(Received via mailing list)
On Behalf Of Farhad Farzaneh:
# Hash's default value object, not a copy of it.  So if you modify that,
# it modifies the default value, which then effects the default
# value for every other new key.

it is defined.

irb(main):014:0> system "qri hash#new"
-------------------------------------------------------------- Hash::new
     Hash.new                          => hash
     Hash.new(obj)                     => aHash
     Hash.new {|hash, key| block }     => aHash
------------------------------------------------------------------------
     Returns a new, empty hash. If this hash is subsequently accessed
     by a key that doesn't correspond to a hash entry, the value
     returned depends on the style of new used to create the hash. In
     the first form, the access returns nil. If obj is specified, this
     single object will be used for all default values. If a block is
     specified, it will be called with the hash object and the key, and
     should return the default value. It is the block's responsibility
     to store the value in the hash if required.

        h = Hash.new("Go Fish")
        h["a"] = 100
        h["b"] = 200
        h["a"]           #=> 100
        h["c"]           #=> "Go Fish"
        # The following alters the single default object
        h["c"].upcase!   #=> "GO FISH"
        h["d"]           #=> "GO FISH"
        h.keys           #=> ["a", "b"]

        # While this creates a new default object each time
        h = Hash.new { |hash, key| hash[key] = "Go Fish: #{key}" }
        h["c"]           #=> "Go Fish: c"
        h["c"].upcase!   #=> "GO FISH: C"
        h["d"]           #=> "Go Fish: d"
        h.keys           #=> ["c", "d"]

=> true


# I don't know, I wouldn't call this the "principle of least
# surprise"... I was plenty surprised!

ruby allows one to change the default value. that is good, imho.
i think the surprise stems fr the fact that it is _too simple to change
the default. hey, it's ruby ;)

kind regards -botp
B92e77600ddf31da8b72c2637431d2cf?d=identicon&s=25 Farhad Farzaneh (clipper)
on 2007-07-04 09:46
Peña, Botp wrote:

>
> ruby allows one to change the default value. that is good, imho.
> i think the surprise stems fr the fact that it is _too simple to change
> the default. hey, it's ruby ;)
>
> kind regards -botp

You're right.  Somehow I missed that in the documentation.... did RTFM,
but obviously not carefully enough.

Still, I do find it pretty surprising.  I got stung when I was actually
using the feature to initialize any entry to an array just to avoid the
business of

hash[key] ||= []

Lesson learned.
6087a044557d6b59ab52e7dd20f94da8?d=identicon&s=25 Peña, Botp (Guest)
on 2007-07-04 10:34
(Received via mailing list)
On Behalf Of Farhad Farzaneh:
# Still, I do find it pretty surprising.  I got stung when I
# was actually
# using the feature to initialize any entry to an array just to
# avoid the
# business of
#
# hash[key] ||= []

just be careful w hash#[]<< method (but not too careful, otherwise, you
would not enjoy ruby :). What you really wanted then was hash#[]=

irb(main):041:0> y = Hash.new
=> {}
irb(main):043:0> y[1]
=> nil
irb(main):044:0> y[1] << "test"
NoMethodError: undefined method `<<' for nil:NilClass
        from (irb):44
        from :0

here, since y.default == nil  or  y[1] == nil, we are actually doing
nil << "test" wc is obviously wrong.

irb(main):045:0> y[1] = "test"
=> "test"

take a look also at hash#default
Ad7805c9fcc1f13efc6ed11251a6c4d2?d=identicon&s=25 Alex Young (regularfry)
on 2007-07-04 11:14
(Received via mailing list)
Farhad Farzaneh wrote:
>
> Still, I do find it pretty surprising.  I got stung when I was actually
> using the feature to initialize any entry to an array just to avoid the
> business of
>
> hash[key] ||= []
>
Apologies if you've already come across this, but I think the correct
way to do what you're trying to do is this:

h = Hash.new(){|h,k| h[k] = []}

The block passed to Hash.new gets called for each new entry in the hash,
  so a new array will be constructed each time.

Hope this helps,
B92e77600ddf31da8b72c2637431d2cf?d=identicon&s=25 Farhad Farzaneh (clipper)
on 2007-07-04 11:22
Alex Young wrote:
> Apologies if you've already come across this, but I think the correct
> way to do what you're trying to do is this:
>
> h = Hash.new(){|h,k| h[k] = []}
>
> The block passed to Hash.new gets called for each new entry in the hash,
>   so a new array will be constructed each time.
>
> Hope this helps,

Sweet.  Now that looks very Ruby'ish!  Thanks.
1fba4539b6cafe2e60a2916fa184fc2f?d=identicon&s=25 unknown (Guest)
on 2007-07-04 13:11
(Received via mailing list)
Hi --

On Wed, 4 Jul 2007, Farhad Farzaneh wrote:

> but obviously not carefully enough.
>
> Still, I do find it pretty surprising.

All you're doing is assigning an object to a certain role.  There's no
implication of automatic duplication of the object.

> I got stung when I was actually
> using the feature to initialize any entry to an array just to avoid the
> business of
>
> hash[key] ||= []

It doesn't do that anyway:

   h = {}
   h.default = []
   p h["blah"]     # []   (default value for undefined key)
   p h             # {}   (no keys are defined)

The default value is for undefined keys, whereas hash[key] ||= []
actually sets a key.


David
B92e77600ddf31da8b72c2637431d2cf?d=identicon&s=25 Farhad Farzaneh (clipper)
on 2007-07-04 18:42
unknown wrote:
> Hi --
>
>
> All you're doing is assigning an object to a certain role.  There's no
> implication of automatic duplication of the object.

I was thinking of it as initialization of the value of the item
accessed, but when you think of it as *the* default object it makes
sense.

>
>> I got stung when I was actually
>> using the feature to initialize any entry to an array just to avoid the
>> business of
>>
>> hash[key] ||= []
>
> It doesn't do that anyway:
>
>    h = {}
>    h.default = []
>    p h["blah"]     # []   (default value for undefined key)
>    p h             # {}   (no keys are defined)
>
> The default value is for undefined keys, whereas hash[key] ||= []
> actually sets a key.
>

The misunderstanding on my part was that I thought it initialized the
key to the default object, rather than returned the actual default
object.  In this context, if the key is automatically initialized to an
empty array, then when I wanted to assign something to it, I wouldn't
have to first make it an array.
E34b5cae57e0dd170114dba444e37852?d=identicon&s=25 Logan Capaldo (Guest)
on 2007-07-05 05:22
(Received via mailing list)
On 7/4/07, Farhad Farzaneh <ff@onebeat.com> wrote:
> => ["this"]
> Posted via http://www.ruby-forum.com/.
>
>
Copying is an interesting thing. For many objects there is a sane,
obvious
way to make a copy. But for many others it is not so clear. Consider for
instance, Hash.new($stdin). What should it mean, in this context, to
"copy"
$stdin? Or consider Hash.new(some_very_large_object). Do you want a copy
all
the time? Luckily, Hash does have a mechanism to acheive what you want:

>> x = Hash.new { |h,k| h[k] = [] }
=> {}
>> x[:test]
=> []
>> x[:test] << 'this'
=> ["this"]
>> x[:bar]
=> []

POLS also is matz.'s POLS.
B92e77600ddf31da8b72c2637431d2cf?d=identicon&s=25 Farhad Farzaneh (clipper)
on 2007-07-05 16:41
Logan Capaldo wrote:
> On 7/4/07, Farhad Farzaneh <ff@onebeat.com> wrote:
>> => ["this"]
>> Posted via http://www.ruby-forum.com/.
>>
>>
> Copying is an interesting thing. For many objects there is a sane,
> obvious
> way to make a copy. But for many others it is not so clear. Consider for
> instance, Hash.new($stdin). What should it mean, in this context, to
> "copy"
> $stdin? Or consider Hash.new(some_very_large_object). Do you want a copy
> all
> the time? Luckily, Hash does have a mechanism to acheive what you want:
>
>>> x = Hash.new { |h,k| h[k] = [] }
> => {}
>>> x[:test]
> => []
>>> x[:test] << 'this'
> => ["this"]
>>> x[:bar]
> => []
>
> POLS also is matz.'s POLS.

Current behavior does (of course) make sense, and my confusion was due
to my misunderstanding of the feature and its intended use.

Thanks to all.
This topic is locked and can not be replied to.