Perl List Interpolation Index

Background

Perldoc for List :: Util suggests that some uses mapcan be replaced with reduceto avoid creating an unnecessary staging list:

For example, to find the total length of all lines in a list, we could use

$total = sum map { length } @strings;

However, this creates a list of temporary integer values, as long as the original list of strings, only to reduce it to one value again. We can more accurately calculate the same result using a block of code that accumulates lengths by writing this instead:

$total = reduce { $a + length $b } 0, @strings;

It makes sense. However, reduceto work in this example, it needs an “identification value” that will be added to the input list:

$total = reduce { $a + length $b } 0, @strings;
#                                  ^^^^^^^^^^^

, 0, @strings , , creaing map?

($scalar, @list) Perl? ? , :

use strict;
use warnings;
use Benchmark qw/cmpthese/;

my @a1 = 1..10;
my @a2 = 1..100;
my @a3 = 1..1000;
my @a4 = 1..10000;
my @a5 = 1..100000;
my @a6 = 1..1000000;

cmpthese(10000, {
    'a1' => sub { my @l = (0, @a1); },
    'a2' => sub { my @l = (0, @a2); },
    'a3' => sub { my @l = (0, @a3); },
    'a4' => sub { my @l = (0, @a4); },
    'a5' => sub { my @l = (0, @a5); },
    'a6' => sub { my @l = (0, @a6); },
});

:

            (warning: too few iterations for a reliable count)
        Rate       a6       a5       a4       a3       a2       a1
a6    17.6/s       --     -90%     -99%    -100%    -100%    -100%
a5     185/s     952%       --     -90%     -99%    -100%    -100%
a4    1855/s   10438%     902%       --     -90%     -99%    -100%
a3   17857/s  101332%    9545%     862%       --     -91%     -98%
a2  200000/s 1135940%  107920%   10680%    1020%       --     -80%
a1 1000000/s 5680100%  540000%   53800%    5500%     400%       --

: (.. 0, @strings ), map reduce?

+4
3

0, @strings

. , SVOP.

. map reduce !

, . map , sum . . ( , .)

, reduce . reduce .

+4

use strict;
use warnings;

use List::Util qw(sum reduce);    
use Benchmark qw(cmpthese);

my @ary = 1..10_000;

sub by_reduce { my $res = reduce { $a + length $b } 0, @ary }

sub by_map { my $res = sum map { length } @ary }

cmpthese(-3, {
    reduce  => sub { by_reduce  },  
    map     => sub { by_map },
});

v5.16

         Rate    map reduce
map     780/s     --   -41%
reduce 1312/s    68%     --

, reduce - .

, , .

, . , .

, map reduce . ,

my @ary = 1..10_000;
# benchmark:
my $r1 = sum map { length } @ary;
my $r2 = sum map { length } (1..5000, 5001..10_000);

, 780/s 782/s, , map . ( , ikegami .)

+3

" , " map, N . , reduce , . , reduce . , reduce , . , reduce , - .

, . , , .


, 0, @strings

. reduce . .

. "sub " "op ", "sub op ".

ops, . . , reduce { ... } 0, @a entersub op. { ... } , ref /, 0 , /, @strings , /. / sub: glob *reduce.

, , . , , ( ).

This means that there is virtually no difference in performance between reduce { ... } @stringsand reduce { ... } 0, @strings. Both create one list, and both add approximately the same number of items to the list / stack.

Exceptions:

  • for (@a)optimized as for* (\@a).
    This saves memory and saves time if the loop exits prematurely.
  • sub f(\@); f(@a)equivalent to &f(\@a).

AFAIK, mapand are grepnot optimized in this way.


More details:

$ perl -MO=Concise,-exec -MList::Util=reduce -e'reduce { ... } @a'
...
3  <0> pushmark s                        <-- Creates list (adds mark to the stack).
4  <$> anoncode[CV ] sRM                 <-- Adds CV to the stack.
5  <1> srefgen sKM/1                     <-- Replaces CV with a ref to the CV.
6  <#> gv[*a] s                          <-- Places *a on the stack.
7  <1> rv2av[t4] lKM/1                   <-- Replaces *a with the contents of @a.
8  <#> gv[*reduce] s                     <-- Places *reduce on the stack.
9  <1> entersub[t5] vKS/TARG             <-- Will remove the entire list from the stack.
...

$ perl -MO=Concise,-exec -MList::Util=reduce -e'reduce { ... } 0, @a'
...
3  <0> pushmark s
4  <$> anoncode[CV ] sRM
5  <1> srefgen sKM/1
6  <$> const[IV 0] sM                    <-- The only difference.
7  <#> gv[*a] s
8  <1> rv2av[t4] lKM/1
9  <#> gv[*reduce] s
a  <1> entersub[t5] vKS/TARG
...
+3
source

Source: https://habr.com/ru/post/1681435/


All Articles