Perl 5 умел копировать списки. Он копирует только столько предметов, сколько находится на левой стороне. Это работает, потому что назначение списка в скалярном контексте дает количество элементов с правой стороны. Таким образом, n
элементы будут созданы регулярным выражением, но они не будут скопированы и удалены, а просто удалены. Вы можете увидеть разницу, которую делает дополнительная копия в простом случае, в тесте ниже.
Что касается эффективности, то итеративное решение часто проще в использовании памяти и ЦП, но это должно быть сопоставлено с краткостью оператора goatse secret. Вот результаты сравнительного анализа различных решений:
naive: 10
iterative: 10
goatse: 10
for 0 items:
Rate iterative goatse naive
iterative 4365983/s -- -7% -12%
goatse 4711803/s 8% -- -5%
naive 4962920/s 14% 5% --
for 1 items:
Rate naive goatse iterative
naive 749594/s -- -32% -69%
goatse 1103081/s 47% -- -55%
iterative 2457599/s 228% 123% --
for 10 items:
Rate naive goatse iterative
naive 85418/s -- -33% -82%
goatse 127999/s 50% -- -74%
iterative 486652/s 470% 280% --
for 100 items:
Rate naive goatse iterative
naive 9309/s -- -31% -83%
goatse 13524/s 45% -- -76%
iterative 55854/s 500% 313% --
for 1000 items:
Rate naive goatse iterative
naive 1018/s -- -31% -82%
goatse 1478/s 45% -- -75%
iterative 5802/s 470% 293% --
for 10000 items:
Rate naive goatse iterative
naive 101/s -- -31% -82%
goatse 146/s 45% -- -75%
iterative 575/s 470% 293% --
Вот код, который его сгенерировал:
#!/usr/bin/perl
use strict;
use warnings;
use Benchmark;
my $s = "a" x 10;
my %subs = (
naive => sub {
my @matches = $s =~ /a/g;
return scalar @matches;
},
goatse => sub {
my $count =()= $s =~ /a/g;
return $count;
},
iterative => sub {
my $count = 0;
$count++ while $s =~ /a/g;
return $count;
},
);
for my $sub (keys %subs) {
print "$sub: @{[$subs{$sub}()]}\n";
}
for my $n (0, 1, 10, 100, 1_000, 10_000) {
$s = "a" x $n;
print "\nfor $n items:\n";
Benchmark::cmpthese -1, \%subs;
}