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

I was playing with a Perl evalbot on freenode today. I came up with the following:

@array = split /\s*/, 'the quick brown fox jumps over the lazy dog';@h +ash{@array}++;print for sort keys %hash;

It is jumbled up like this because you have to get it all in one line for the bot.

It was pointed out to me by someone lurking in the bot room that @hash{@array}++ does not do what I think it does. He was right. I assumed that it created a new hash element for each of the elements of @array with the key being a value from @array and the value being 1.

I realized that I was wrong, but I wondered why it still worked. I took the discussion into #perl to get more input.

I was told by several users that it did the same as $hash{$array[-1]}++, and that I was wrong. No need to look any further.

The problem is that I then tested it both ways and found that the output was not the same. The difference is that $hash{$array[-1]}++ takes the last element of @array and creates a single hash element with value 1 and @hash{@array}++ does the same but it also creates hash elements for all of the other elements of @array with value undef.

A hash element with a value of undef is apparently different that a hash element that has never been created.

I pointed this out on the channel and I was told that while I was correct on this point, I should not use this property since it is probably a side effect of how hash slices are implemented. Also, it may confuse others.

I was just curious to get other opinions on this property regarding why it works and if it should be used in the above fashion if understood for what it really does or not.

Code tags added by GrandFather

Replies are listed 'Best First'.
Re: postfix incrementing of hash slice
by BrowserUk (Patriarch) on Oct 01, 2006 at 04:47 UTC

    If you just want to autovivify the hash keys without actually assigning any value, you can do that:

    [0] Perl> @array = split /\s*/, 'the quick brown fox jumps over the la +zy dog';; [0] Perl> @hash{ @array } =( );; [0] Perl> print sort keys %hash;; a b c d e f g h i j k l m n o p q r s t u v w x y z

    You can also do undef @hash{ @list }; which benchmarks very slightly faster.

    Whether you think "people will be confused" by either of these is your own call, or that of your employer.


    Examine what is said, not who speaks -- Silence betokens consent -- Love the truth but pardon error.
    Lingua non convalesco, consenesco et abolesco. -- Rule 1 has a caveat! -- Who broke the cabal?
    "Science is about questioning the status quo. Questioning authority".
    In the absence of evidence, opinion is indistinguishable from prejudice.
Re: postfix incrementing of hash slice
by ysth (Canon) on Oct 01, 2006 at 05:24 UTC
    The ++ clearly applies scalar, lvalue context to the hash slice, and the resulting behavior shouldn't be a surprise;
    sub lst:lvalue {$hash{foo},$hash{bar},$hash{baz}} (lst())++
    has a similar, but perhaps less unexpected, effect. But I don't believe the effect of putting a hash slice in scalar (much less scalar lvalue) context is anywhere defined (like so much else in perl). I wouldn't use it; I could see it triggering a warning or even an error in future perls, just as does:
    ($hash{foo},$hash{bar},$hash{baz})++
Re: postfix incrementing of hash slice
by shmem (Chancellor) on Oct 01, 2006 at 09:00 UTC

    Consider:

    %hash; # a hash $hash{$foo} = 'bar'; # used in scalar context @ary = qw(foo bar baz); $hash{@ary}++; # key is scalar(@ary) here: $hash{3} == 1; @hash{@ary} = (1,2,3); # hash is now (foo=>1,bar=>2,baz=3) @hash{@ary}++; # $hash{baz} == 4; # why?

    What you want is

    @hash{@array} = (1) x scalar(@array);

    If you use a hash slice, you use the hash in list context. So assign it a list.

    <update> If you say @hash{@ary}++, the hash iterator points to the hash key corresponding to the last element of @ary after the slice has been set up. The value of that key is incremented. </update>

    <update> fixed typo (better: bug, negligence) at x - thanks cdarke ;-)
    fixed another typo in first code block </update>

    --shmem

    _($_=" "x(1<<5)."?\n".q·/)Oo.  G°\        /
                                  /\_¯/(q    /
    ----------------------------  \__(m.====·.(_("always off the crowd"))."·
    ");sub _{s./.($e="'Itrs `mnsgdq Gdbj O`qkdq")=~y/"-y/#-z/;$e.e && print}
      This might be a typo in shmem's code, but the value '1' should be in list context. There is no need for 'scalar' since the rhs of the x operator can only be a numeric scalar. So I prefer:
      @hash{@array} = (1) x @array;
      With a suitable comment, since it is not particularly obvious.
        Note that this is one of the few places where adding () changes context - and I prefer to think of it as list-context ()x being a different operator than x. AIUI this is fixed in perl6 to actually have have different characters for the two operators.
Re: postfix incrementing of hash slice
by cdarke (Prior) on Oct 01, 2006 at 11:28 UTC
    "A hash element with a value of undef is apparently different that a hash element that has never been created."
    I wonder if you really mean 'value' here. I often use the following when I want a look-up table:
    @hash{@array}=(undef) x @array;
    To find if a key exists use 'exists', to find if a key has a value use 'defined'.
    %hash = undef; produces a hash with a single (weird) key which is undef, and no values (the correct way to get rid of a hash is undef %hash). Every hash value has a key, but not every key need have a value, it can be undef. But there can be only one 'undef' key.
      %hash = undef; produces a hash with a single (weird) key

      The key gets upgraded to a string, so it is really the null string:

      use warnings; use strict; my %hash = undef; use Data::Dumper;print Dumper \%hash; __END__ Odd number of elements in hash assignment at - line 1. Use of uninitialized value in list assignment at - line 1. $VAR1 = { '' => undef };

      --
      David Serrano