Обновление:
Я подумал об этом немного больше и придумал это решение, используя встроенный блок кода, который почти в три раза быстрее, чем решение grep
:
use 5.010;
use warnings;
use strict;
{my @pos;
my $push_pos = qr/(?{push @pos, $-[0]})/;
sub with_code {
my ($re, $str) = @_;
@pos = ();
$str =~ /(?:$re)$push_pos(?!)/;
@pos
}}
и для сравнения:
sub with_grep { # old solution
my ($re, $str) = @_;
grep {pos($str) = $_; $str =~ /\G(?:$re)/} 0 .. length($str) - 1;
}
sub with_while { # per Michael Carman's solution, corrected
my ($re, $str) = @_;
my @pos;
while ($str =~ /\G.*?($re)/) {
push @pos, $-[1];
pos $str = $-[1] + 1
}
@pos
}
sub with_look_ahead { # a fragile "generic" version of Sean's solution
my ($re, $str) = @_;
my ($re_a, $re_b) = split //, $re, 2;
my @pos;
push @pos, $-[0] while $str =~ /$re_a(?=$re_b)/g;
@pos
}
Бенчмаркинг и проверка работоспособности:
use Benchmark 'cmpthese';
my @arg = qw(aa aaaabbbbbbbaaabbbbbaaa);
my $expect = 7;
for my $sub qw(grep while code look_ahead) {
no strict 'refs';
my @got = &{"with_$sub"}(@arg);
"@got" eq '0 1 2 11 12 19 20' or die "$sub: @got";
}
cmpthese -2 => {
grep => sub {with_grep (@arg) == $expect or die},
while => sub {with_while (@arg) == $expect or die},
code => sub {with_code (@arg) == $expect or die},
ahead => sub {with_look_ahead(@arg) == $expect or die},
};
Какие отпечатки:
Rate grep while ahead code
grep 49337/s -- -20% -43% -65%
while 61293/s 24% -- -29% -56%
ahead 86340/s 75% 41% -- -38%
code 139161/s 182% 127% 61% --