Why Perl's "map" function gives an "Insufficient arguments for map" error

This is what I do not understand.

This script works correctly (note the concatenation in the functin map):

#!/usr/bin/perl use strict; use warnings; use Data::Dumper; my %aa = map { 'a' . '' => 1 } (1..3); print Dumper \%aa; __END__ output: $VAR1 = { 'a' => 1 }; 

But without concatenation, the card does not work. Here is the script I expect to work, but it is not:

 #!/usr/bin/perl use strict; use warnings; use Data::Dumper; my %aa = map { 'a' => 1 } (1..3); print Dumper \%aa; __END__ output: Not enough arguments for map at e.pl line 7, near "} (" syntax error at e.pl line 7, near "} (" Global symbol "%aa" requires explicit package name at e.pl line 9. Execution of e.pl aborted due to compilation errors. 

Could you explain this behavior?

+7
dictionary perl
source share
4 answers

Perl uses heuristics to decide if you are using:

 map { STATEMENTS } LIST; # or map EXPR, LIST; 

Because although "{" is often the beginning of a block, it could also be the beginning of hashref.

These heuristics do not go too far in the stream of tokens (2 IIRC tokens).

You can force "{" to be interpreted as a block using:

 map {; STATEMENTS } LIST; # the semicolon acts as a disambigator 

You can force "{" to be interpreted as a hash using:

 map +{ LIST }, LIST; # the plus sign acts as a disambigator 

grep suffers in a similar way. (Technically, it also does that hashref can be given as an argument, which will then be pulled together and processed as if it were a file name. It's just weird.)

+12
source share

In the Documentation for map :

Since Perl does not look forward to closing } , he must guess what he is dealing with based on what he finds immediately after { . This is usually correct, but if it is not, then he won’t understand that something is not right until he reaches }

Providing examples:

 %hash = map { "\L$_" => 1 } @array # perl guesses EXPR. wrong %hash = map { +"\L$_" => 1 } @array # perl guesses BLOCK. right 

So adding + will give you the same thing as the first example you specified

 my %aa = map { +'a'=> 1 } (1..3); 
+6
source share

The perl manpage entry for map () explains this:

  "{" 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 it dealing with based on 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 comma at end

 to get a list of anonymous hashes each with only one entry
 apiece.

Based on this, in order to get rid of kludge concatenation, you need to configure your syntax to one of them:

 my %aa = map { +'a' => 1 } (1..3); my %aa = map { ('a' => 1) } (1..3); my %aa = map +( 'a' => 1 ), (1..3); 
+3
source share

The brackets are a bit ambiguous in the context of the map. They may be surrounded by a block as you intend, or they may be an anonymous hash constructor. There is some fuzzy logic in the Perl parser that tries to guess who you mean.

The second case is more like an anonymous perl hash.

See perldoc for a map that explains this and gives some workarounds.

+2
source share

All Articles