tags:

views:

390

answers:

4

I have some questions about Perl's "map" function.

Specifically:

  • How does

    %hash = map {$_ => 1} @array

    create a hash mapping array's elements to 1? How does block return a list of two elements? I thought block returns its last value. Does => implicitly create a list, as opposed to "," that returns its right argument?

  • Why does

    %hash = map ($_ => 1), @array

    not work? I am trying to return a list of two elements here... And how does prepending "+" before ")" fix it, from the parser's point of view?

A: 

The first argument is a code-block or subroutine. Code blocks must be surrounded by {}. They work not-quite like lambdas, but pretty darn close.

Edit: No clue why adding the + after the 1 works. Perhaps it's compiling to a code block in some weird way? This doesn't seem to match perldoc.

+8  A: 

Question 1: map blocks are run list context, and as such are allowed to return zero, one or more values. map returns them all. "," or "=>"return their right side in scalar context, but both sides in list context. See perlop for the details.

Question 2: %hash = map ($_ => 1), @array is interpreted as %hash = (map($_, 1), @array). In other words, it returns (1, @array). In %hash = map +($_ => 1), @array, the + indicates that the () doesn't refer to an argument list, so it is interpreted as map(+($_ => 1), @array);

The lesson of the day: always use accolades around your map expression, that way you won't be bitten by these kinds of issues.

Leon Timmermans
Could you add a bit more to your answer to tell me how is block returning multiple values in the first case?
Arkadiy
The context you're not grasping here seems to be context. Perl expressions can have different contexts: void, scalar and list. You're thinking in scalar context, while what's going on here is list context.
Leon Timmermans
I did not realize , returns both sides in the list context. Thank you!
Arkadiy
Oops. The first context in my previous post should have been concept...
Leon Timmermans
I have never seen accolades used to mean braces.
Mr. Muskrat
muskat: such characters often have many names.
Leon Timmermans
Another good reason to avoid the "map +($_ => 1), @array" form is that this form contradicts Perl's default behaviour of flattening lists -- in nearly all cases, "func +($_ => 1), @array" is identical to "func($_, 1, @array)", though that's not the case with the map call here.
j_random_hacker
A: 

Map applies the block to each value in the array/list and creates a new list. => is syntactic sugar to show that the list consists of key/value pairs (IIRC, it would work with ordinary comma as well). When you assign a list of alternatig key/value pairs to a hash variable, a hash table is created.

The other variant does not work because it is not a code block. I don't know about +.

zvrba
Your last paragraph is incorrect. A code block is not required as first argument.
Leon Timmermans
+4  A: 

Adding to @Leon's correct answers to help you with your second question:

from perlop

Unary "+" has no effect whatsoever, even on strings. It is useful syntactically for separating a function name from a parenthesized expression that would otherwise be interpreted as the complete list of function arguments.

You can use deparse to see how perl interprets each:

perl -MO=Deparse,-p -e "%h = map ($_ => 1), @a;"
(((%h) = map($_, 1)), @a);

where as

perl -MO=Deparse,-p -e "%h = map +($_ => 1), @a;"
((%h) = map(($_, 1), @a));

I hope this helps

Pat
Deparse - another great thing learned today. Thanks.
Arkadiy