Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why does Perl function "map" give the error "Not enough arguments for map"

Here is the thing I don't understand.

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

#!/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 map does not work. Here is the script I expect to work, but it does 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.

Can you please explain such behaviour?

like image 547
bessarabov Avatar asked Jan 09 '14 22:01

bessarabov


2 Answers

Perl uses heuristics to decide whether you're using:

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

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

These heuristics don't look ahead very far in the token stream (IIRC two 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 similarly. (Technically so does do, in that a hashref can be given as an argument, which will then be stringified and treated as if it were a filename. That's just weird though.)

like image 87
tobyink Avatar answered Sep 17 '22 16:09

tobyink


Per the Documentation for map:

Because Perl doesn't look ahead for the closing } it has to take a guess at which it's 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 }

Giving the 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 as the first example you've given

my %aa = map {  +'a'=> 1 } (1..3);
like image 31
chrsblck Avatar answered Sep 17 '22 16:09

chrsblck