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

Hello all,

This one might be a very trivial to you, but sort of interesting to me. This two lines have the same effect, but I am not sure about the difference between these two kinds of syntax. Any idea?

@vals = map($_*5, @vals); @vals = map{$_*5} @vals;
Thanks.

Replies are listed 'Best First'.
Re: syntax of map operator
by Marshall (Canon) on Jan 24, 2010 at 13:05 UTC
    From what I can see, they do the same thing with the easier to understand map{$_*5} @vals being very, very slightly slower as far as execution time. Doesn't appear to be enough slower to sacrifice the understandability.
    #!/usr/bin/perl -w use strict; my @values = qw(1 2 3 4 5 6 7 8 9 10 11 12 13 14); print "Just to show that these do the same\n"; my @vals1 = map($_*5, @values); print "@vals1\n"; my @vals2 = map{$_*5} @values; print "@vals2\n"; #prints: #Just to show that these do the same #5 10 15 20 25 30 35 40 45 50 55 60 65 70 #5 10 15 20 25 30 35 40 45 50 55 60 65 70 use Benchmark; timethese (1000000, { paren => q{ my @values = qw(1 2 3 4 5 6 7 8 9 10 11 12 13 14); my @vals = map($_*5, @values); }, block => q{ my @values = qw(1 2 3 4 5 6 7 8 9 10 11 12 13 14); my @vals = map{$_*5} @values; }, } ); __END__ Just to show that these do the same 5 10 15 20 25 30 35 40 45 50 55 60 65 70 5 10 15 20 25 30 35 40 45 50 55 60 65 70 Benchmark: timing 1000000 iterations of block, paren... block: 20 wallclock secs (19.38 usr + 0.00 sys = 19.38 CPU) @ 51612.90/s (n=1000000) paren: 20 wallclock secs (19.30 usr + 0.00 sys = 19.30 CPU) @ 51821.53/s (n=1000000) Just to show that these do the same 5 10 15 20 25 30 35 40 45 50 55 60 65 70 5 10 15 20 25 30 35 40 45 50 55 60 65 70 Benchmark: timing 1000000 iterations of block, paren... block: 20 wallclock secs (19.44 usr + 0.00 sys = 19.44 CPU) @ 51445.62/s (n=1000000) paren: 20 wallclock secs (19.39 usr + 0.00 sys = 19.39 CPU) @ 51570.32/s (n=1000000)
Re: syntax of map operator
by ikegami (Patriarch) on Jan 24, 2010 at 09:40 UTC

    The BLOCK version creates a scope. That adds overhead, so the BLOCK version is a bit slower. I don't remember how much, but it's surely noting substantial.

    On the other hand, the BLOCK version allows the use of multiple statements, including variable declarations. It's usually easier to read too.

    Update: I since discovered that both versions add a scope. (See higher up post.) That doesn't change the fact that map BLOCK is a bit slower; it just means I don't know why.

      I since discovered that both versions add a scope.
      Yes, but they aren't the same scopes. The EXPR variant doesn't create a lexical scope; the BLOCK variant does:
      $ perl -Mstrict -wcE 'my @x = map my $x = $_, 2; say $x' -e syntax OK $ perl -Mstrict -wcE 'my @x = map {my $x = $_} 2; say $x' Global symbol "$x" requires explicit package name at -e line 1. -e had compilation errors.
      I'm not quite sure what kind of scoping effects happen.
      $ perl -Mstrict -wE 'my @x = map my $x = $_, 2; say $x' Use of uninitialized value $x in say at -e line 1. $ perl -Mstrict -wE 'my $x; my @x = map $x = $_, 2; say $x' 2
      So, if we declare $x in the first argument of map, $x exists afterwards, but doesn't have a defined value. If declared before the map, it keeps the last assigned value.

      I guess that's more an artifact of the implementation than that it was designed to work this way.

        I guess that's more an artifact of the implementation than that it was designed to work this way.

        I fully agree. Compile-time lexical scopes and run-time lexical scopes should be the same. All of these are in the same bag:

        • my $x if ...;
        • my $x for ...;
        • map my $x, ...;
        • ... and my $;x

        Just don't.

      Intuitively, I feel that the block must add a wider scope than the expression: for the latter, the scope effect is limited to affect just $_, for the block, you can add lexical variables of your own.

      Put it this way: the expression can use a "lite" scope.

      caveat: I have no idea if this is actually the case!

        Intuitively, I feel that the block must add a wider scope than the expression

        I think you mean narrower (i.e more restrictive) scope. Lexicals declared in the block are not visible outside of the map. Lexicals declared in the expression are visible outside of the map

        I agree. That narrower scope is the compile-time scope I mentioned earlier. map BLOCK has one. map EXPR, doesn't (unless EXPR includes a do{} or something).

Re: syntax of map operator
by Anonymous Monk on Jan 24, 2010 at 09:35 UTC
    B::Concise
    perl -MO=Concise -e " @vals = map($_*5, @vals); " perl -MO=Concise -e " @vals = map{$_*5} @vals; "
    side by side
    $ perl -MO=Concise -e " @vals = map($_*5, @vals); " $ perl -MO=Con +cise -e " @vals = map{$_*5} @vals; " g <@> leave[1 ref] vKP/REFC ->(end) g <@> leave[1 +ref] vKP/REFC ->(end) 1 <0> enter ->2 1 <0> enter + ->2 2 <;> nextstate(main 1 -e:1) v:{ ->3 2 <;> nexts +tate(main 2 -e:1) v:{ ->3 f <2> aassign[t8] vKS/COMMON ->g f <2> aassi +gn[t9] vKS/COMMON ->g - <1> ex-list lK ->c - <1> ex +-list lK ->c 3 <0> pushmark s ->4 3 <0> + pushmark s ->4 8 <|> mapwhile(other->9)[t7] lK ->c 8 <|> + mapwhile(other->9)[t8] lK ->c 7 <@> mapstart lK ->8 7 +<@> mapstart lK* ->8 4 <0> pushmark s ->5 4 + <0> pushmark s ->5 - <1> null lK/1 ->5 - + <1> null lK/1 ->5 b <2> multiply[t4] sK/2 ->8 - + <1> null lK/1 ->8 - <1> ex-rv2sv sK/1 ->a - + <@> scope lK ->8 9 <#> gvsv[*_] s ->a - + <0> ex-nextstate v ->9 a <$> const[IV 5] s ->b b + <2> multiply[t4] sK/2 ->- 6 <1> rv2av[t6] lKM/1 ->7 - + <1> ex-rv2sv sK/1 ->a 5 <#> gv[*vals] s ->6 9 + <#> gvsv[*_] s ->a - <1> ex-list lK ->f a + <$> const[IV 5] s ->b c <0> pushmark s ->d 6 + <1> rv2av[t6] lKM/1 ->7 e <1> rv2av[t2] lKRM*/1 ->f 5 + <#> gv[*vals] s ->6 d <#> gv[*vals] s ->e - <1> ex +-list lK ->f -e syntax OK c <0> + pushmark s ->d e <1> + rv2av[t2] lKRM*/1 ->f d +<#> gv[*vals] s ->e -e syntax OK
    I'm no expert, but it looks like the difference is a new scope being introduced, like
    @ARGV and $ARGV[0] =~ /asdf/ and print;
    versus
    if(@ARGV and $ARGV[0] =~ /asdf/){ print; }
      It's easier to see the diff here:
      $ diff -u \ <( perl -MO=Concise,-exec -e'@vals = map $_*5, @vals;' 2>&1 ) \ <( perl -MO=Concise,-exec -e'@vals = map { $_*5 } @vals;' 2>&1 ) --- /proc/self/fd/63 2010-01-24 01:45:47.147224485 -0800 +++ /proc/self/fd/62 2010-01-24 01:45:47.123224005 -0800 @@ -1,18 +1,19 @@ -e syntax OK 1 <0> enter -2 <;> nextstate(main 1 -e:1) v +2 <;> nextstate(main 2 -e:1) v 3 <0> pushmark s 4 <0> pushmark s 5 <#> gv[*vals] s 6 <1> rv2av[t6] lKM/1 -7 <@> mapstart lK/2 -8 <|> mapwhile(other->9)[t7] lK/1 +7 <@> mapstart lK*/2 +8 <|> mapwhile(other->9)[t8] lK/1 9 <#> gvsv[*_] s a <$> const[IV 5] s b <2> multiply[t4] sK/2 +- <@> scope lK goto 8 c <0> pushmark s d <#> gv[*vals] s e <1> rv2av[t2] lKRM*/1 -f <2> aassign[t8] vKS/COMMON +f <2> aassign[t9] vKS/COMMON g <@> leave[1 ref] vKP/REFC

      It's very unclear what that means. The leading "-" means it's not executed, yet it's still showing in the -exec listing. What does that OP do anyway? It's executed at the *end* of the callback code. Looking at the source doesn't help

      PP(pp_scope) { dVAR; return NORMAL; }

      Without a lot more digging, B::Concise's output does really help. On the other hand, it's very easy to show that a scope does get created for map BLOCK but not for map EXPR,

      ...or not.

      $ perl -wle'use strict; map +(my $x = $_), 2; print $x' Use of uninitialized value in print at -e line 1.

      It seems a run-time scope is created for map EXPR,.

      (Compile-time scope affects the visibility of lexical variables. Run-time scopes control when they get cleared. They should be the same, but sometimes they're not.)

        $ perl -wle'use strict; map +(my $x = $_), 2; print $x' Use of uninitialized value in print at -e line 1.
        It seems a run-time scope is created for map EXPR,.

        Incorrect.    What is happening is the same thing that happens when you do that with a foreach statement modifier:

        $ perl -wle'use strict; my $x = $_ for 2; print $x' Use of uninitialized value $x in print at -e line 1.

        Braces or file boundaries are the only things that can create scope.

      Accidental dup. Please ignore.