http://qs321.pair.com?node_id=601884


in reply to Re: Hash assignments using map
in thread Hash assignments using map

Hi friedo

I'm still not clear on this. Isn't that what I'm doing in here:

my %hash; my @keys = qw{ a b c d }; foreach ( @keys ) { $hash{$_}++; }

Can you clarify for me?

thanks - njcodewarrior

Replies are listed 'Best First'.
Re^3: Hash assignments using map
by varian (Chaplain) on Feb 24, 2007 at 17:57 UTC
    njcodewarrior,
    You're right in that the map function acts like a loop. Where is gets tricky is when you assign the output of the map function to a hash, because the hash assignment operation evaluates two elements at a time. So Perl does the equivalent of this:
    my $i=0; while ($i<$#group) { $hash{$group[$i]}=$group[$i+1]; $i+=2; }
    As other monkers already pointed out the "odd number of elements" warning will be generated when you feed the hash with a group that has an odd number of elements.

    Now if we look at the map function itself in your code

    @destination = map {$_++} @source
    The effect of the above code is that the elements are copied one by one. At the same time the element in the source (!) group is changed/incremented. Thatīs probably not what you wanted, right?

    If the destination of the map function is a hash then commonly the map is used to produce 2 elements at a time in a list fashion:

    .... = map { ("key$_" , "value$_") } .....
    For clarity purpose the comma operator is typically replaced by a '=>' operator to indicate that we mean to produce something for a hash.
    .... = map { ("key$_" => "value$_") } .....
    I'm not sure what you hoped to achieve through your code but hopefully this shows why your code behaved as it did

      Thanks varian. This makes sense.

      I want to quickly create a hash with keys that are the elements of the list. All I want access to is the keys...I don't care what the values are. I can then use exists to see if those keys either do or do not exist in another hash.

      Thanks again!

        All I want access to is the keys...
        ok, but a hash has a key and a value. So your code should be:
        my @keys = qw{ a b c d }; my %hash = map { $_,1 } @keys;
        if you want your keys unaltered. If you want a "string increment" ($_="a";$_++; # $_ is b now) its ok to use $_++.

        In the loop

        my %hash; foreach ( @keys ) { $hash{$_}++; }

        you are also using two variables - in a shortcut:

        1. you create a new hash slot with key $_
        2. you increment (the previously undefined) value for that key

        So, it's the same thing with keys and values here. With map iterating over an array of keys, you must provide a value to have pairs to be stored as (key,value) in a hash.

        --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}
Re^3: Hash assignments using map
by mk. (Friar) on Feb 24, 2007 at 16:42 UTC
    the first statement takes the elements from the array @keys, increments them and adds to the hash %hash.
    first point is, you only have letters, no numbers, so Perl won't "increment" them, and will just add them as they already are to the hash.
    secont point, the hash takes two elements to populate a key-value pair. for example, a hash %h = (a => 1, b => 2, c => 3) could also be written as %h = qw{a 1 b 2 c 3}.
    the second statement takes each element from the array as a key of the hash, incrementing its value each time it's processed (which means if you have twice the element "a" in the array, the value of the key "a" in the hash will be 2 etc). if you want to use map to do that, it'd like map { $hash{$_}++ } @keys.

    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    *women.pm
      first point is, you only have letters, no numbers, so Perl won't "increment" them

      Really?

      $ perl -le '$c = "a"; print ++$c' b

      Check perlop:

      The auto-increment operator has a little extra builtin magic to it. ... If, however, the variable has been used in only string contexts since it was set, and has a value that is not the empty string and matches the pattern "/^[a-zA-Z]*[0-9]*\z/", the increment is done as a string, preserving each character within its range, with carry ...

      Update: Though you're actually right, but only by coincidence. Using the postfix autoincrement won't make the hash elements incremented.

      $ perl -Mstrict -MData::Dumper -we 'my @a = qw(a b c d); \ my %h = map { $_++ } @a; print Dumper \%h' $VAR1 = { 'c' => 'd', 'a' => 'b' };


      --chargrill
      s**lil*; $*=join'',sort split q**; s;.*;grr; &&s+(.(.)).+$2$1+; $; = qq-$_-;s,.*,ahc,;$,.=chop for split q,,,reverse;print for($,,$;,$*,$/)
        my bad!
        it actually increments the elements of the array, but adds them unchanged to the hash.
        thanks for pointing that out, chargrill++! :)

        ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
        *women.pm