muntfish has asked for the wisdom of the Perl Monks concerning the following question:

I've just discovered some odd behaviour in some code written for perl 5.004_xxx but now running under 5.8.0. A hash is being initialised like this:

%rec_counts={};

and later on, updated like this:

while (defined($line = <F>)) { $rec_type = substr($line, 19, 2); $rec_counts{$rec_type} += 1; }

And finally we print out the record types using:

foreach $key (sort keys(%rec_counts)) { print "Record count of $key is $rec_counts{$key}\n"; }

This gives me the output:

Record count of 00 is 1 Record count of HASH(0x4000473c) is Record count of T is 315

Now, under perl 5.004_x it does not print the middle line, I just get the "real" record types 00 and T. I've narrowed this down to the "initialisation" which behaves correctly if I change it to %rec_counts=(). So I guess I have two questions:

What exactly changed between 5.004 and 5.8.0?

What is the {} syntax doing exactly in 5.8.0? The docs suggest it is creating an empty hash but maybe I'm reading it wrong...

Any insight into this would be most appreciated, thanks.

Replies are listed 'Best First'.
Re: Construction/initialisation of empty hash
by broquaint (Abbot) on Apr 27, 2004 at 14:39 UTC
    The {} operator creates a new anonymous hash reference, so when you assign {} to a hash you're really creating a key of the stringified anonymous hash reference. If you run your code with warnings or -w you'll see a helpful warning e.g
    $ perl5.00504 -MData::Dumper -we '%h={};$h{a}=1;print Dumper \%h' Reference found where even-sized list expected at -e line 1. $VAR1 = { 'a' => 1, 'HASH(0x8922a64)' => undef };
    See. perlref and perldata for more info on the {} operator. As for the changes of output between perl versions, I can't seem to replicate them as the above example illustrates.
    HTH

    _________
    broquaint

      Thanks, that's a helpful one-liner.

      Perl 5.8.0 gives the output you show, however, my older version (which is 5.004_04) gives:

      $ /opt/perl5/bin/perl -MData::Dumper -we '%h={};$h{a}=1;print Dumper \ +%h' Odd number of elements in hash list at -e line 1. $VAR1 = { 'a' => 1 };
        Just speculating; did perl used to drop off odd elements in hash initialization instead of adding an implied undef to make things even out?
Re: Construction/initialisation of empty hash
by dragonchild (Archbishop) on Apr 27, 2004 at 14:32 UTC
    It's creating a key/value pair with the reference address to an empty hashref as the key and undef as the value. This behavior is deprecated, as you would have noticed had you enabled warnings. (Either with the -w flag or with use warnings; somewhere above that initialization line.)

    You'll probably want to add use strict; at the top and work through the warnings errors. Trust me.

    ------
    We are the carpenters and bricklayers of the Information Age.

    Then there are Damian modules.... *sigh* ... that's not about being less-lazy -- that's about being on some really good drugs -- you know, there is no spoon. - flyingmoose

      Thanks for the quick reply!

      In the original module, I do have use strict; at the top. Did you mean to say "You'll probably want to add use warnings; at the top..." in your second paragraph?

      Just using the -w flag for now, this does show warnings under both 5.004 and 5.8.0, however it would still be interesting to know how/why the behaviour has changed.

        I meant "errors", not "warning". Original node updated.

        Usually, one has the my as part of the variable initialization, like my %foo = ();, not separately. Perl is closer to C++ and Java than C, in that regard. Also, you can just say my %foo; and it will work as expected.

        As for why it changed ... I have no idea. 5.004_xx is somewhat different than 5.005_xx and above, in any number of ways. I wouldn't look to compare the two. (The way to think about them is that 5.004 is really 5.4, as compared to 5.6 and 5.8.)

        ------
        We are the carpenters and bricklayers of the Information Age.

        Then there are Damian modules.... *sigh* ... that's not about being less-lazy -- that's about being on some really good drugs -- you know, there is no spoon. - flyingmoose

Re: Construction/initialisation of empty hash
by hv (Prior) on Apr 27, 2004 at 17:37 UTC

    Checking, I find that perl5.004 appears to throw away the last key whenever a hash is initialised from a list with an odd number of elements, whereas perl5.004_05 and later act as if there were an extra undef at the end of the list to make it even.

    As a result, %a = (1, 2, 3) leaves %a containing one key ('1') in perl5.004 and two keys ('1', '3') in newer perls. I guess this came in as a bug fix sometime after the initial perl5.004 release, but I don't know exactly when, and I don't have any of the intermediate versions to check.

    Even perl5.004 warns "Odd number of elements in hash list", so this is just another reason always to turn on warnings. :)

    Hugo

      Thanks hv. The change must have been between 5.004_04 and 5.004_05 since with my 5.004_04 I get the old style behaviour.

      Totally agree about warnings - but this code wasn't written by me! It has been in production for several years (with warnings turned off!) and this issue has only come to light just now with an upgrade to 5.8.0.

      Anyhow since the solution is to change from {} to () and it only happens in 2 or 3 places, I'm not too worried about it.