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

I've become quite fond of the special abilties of the so-called Perl 5 "magical arrow/comma" operator =>, but now it's acting all strange on me. This, in Perl 5.6.1:
#!/usr/bin/perl -w use strict; my $foo = "foo"; my $bar = "bar"; my %hfoo1 = ( "$foo$bar" => "foobar", # Works $bar.$foo => "barfoo", # Works ); my $hfoo2 = { "$foo$bar" => "foobar", # Works $bar.$foo => "barfoo", # Works }; my %hfoo3 = ( map { $foo.$bar => "foobar" } 1, # Works ); my %hfoo4 = ( map { "$foo$bar" => "barfoo" } 1, # Syntax error? # ^^^ );
I'm kind of stumped. I spent a bit of time bouncing around between brackets with % to see if it was something capital-D Dumb, but no such luck.

Am I missing the boat here? This could be just one of those things you can't do.

Replies are listed 'Best First'.
Re: Magical Arrow Not So Magical?
by ChemBoy (Priest) on May 30, 2002 at 03:48 UTC

    As it happens, I think I did what Perl thinks you're trying to do just a couple nodes ago. :-) The key is that this is valid:

    my @hashrefs = map { foo => $_ }, 1..5;

    I suspect that the parser gets to the end of your map block and decides based on its simplicity that it's supposed to be a hashref: therefore, it decides that you're using the map EXPR, LIST syntax instead of the map BLOCK LIST, and expects a comma after the closing brace.

    The simplest solution is to add parentheses inside the block, around the list you're trying to return. The next simplest is to actually do what it thinks you're doing, and go to the EXPR form:

    map {( "$foo$bar" => "barfoo" )} 1, ; #no more syntax err +or :-) map +( "$foo$bar" => "barfoo" ), 1, ; #note unary + #to avoid "function +-like" #interpretation of +()



    If God had meant us to fly, he would *never* have given us the railroads.
        --Michael Flanders

Re: Magical Arrow Not So Magical?
by Anonymous Monk on May 30, 2002 at 04:12 UTC
    perldoc -f map
        map BLOCK LIST
        map EXPR,LIST
                Evaluates the BLOCK or EXPR for each element of LIST (locally
                setting "$_" to each element) and returns the list value
                composed of the results of each such evaluation. In scalar
                context, returns the total number of elements so generated.
                Evaluates BLOCK or EXPR in list context, so each element of LIST
                may produce zero, one, or more elements in the returned value.
    
                    @chars = map(chr, @nums);
    
                translates a list of numbers to the corresponding characters.
                And
    
                    %hash = map { getkey($_) => $_ } @array;
    
                is just a funny way to write
    
                    %hash = ();
                    foreach $_ (@array) {
                        $hash{getkey($_)} = $_;
                    }
    
                Note that "$_" is an alias to the list value, so it can be used
                to modify the elements of the LIST. While this is useful and
                supported, it can cause bizarre results if the elements of LIST
                are not variables. Using a regular "foreach" loop for this
                purpose would be clearer in most cases. See also the grep entry
                elsewhere in this document for an array composed of those items
                of the original list for which the BLOCK or EXPR evaluates to
                true.
    
                "{" starts both hash references and blocks, so "map { ..." could
                be either the start of map BLOCK LIST or map EXPR, LIST. Because
                perl doesn't look ahead for the closing "}" it has to take a
                guess at which its dealing with based what it finds just after
                the "{". Usually it gets it right, but if it doesn't it won't
                realize something is wrong until it gets to the "}" and
                encounters the missing (or unexpected) comma. The syntax error
                will be reported close to the "}" but you'll need to change
                something near the "{" such as using a unary "+" to give perl
                some help:
    
                    %hash = map {  "\L$_", 1  } @array  # perl guesses EXPR.  wrong
                    %hash = map { +"\L$_", 1  } @array  # perl guesses BLOCK. right
                    %hash = map { ("\L$_", 1) } @array  # this also works
                    %hash = map {  lc($_), 1  } @array  # as does this.
                    %hash = map +( lc($_), 1 ), @array  # this is EXPR and works!
    
                    %hash = map  ( lc($_), 1 ), @array  # evaluates to (1, @array)
    
                or to force an anon hash constructor use "+{"
    
                   @hashes = map +{ lc($_), 1 }, @array # EXPR, so needs , at end
    
                and you get list of anonymous hashes each with only 1 entry.