Ну, я нашел способ, который более чем в 30 раз быстрее, хотя, возможно, это обман. Я включил код Benchmark.pm для его тестирования, так как вы, очевидно, не знакомы с ним.
Точка отсчета:
Rate Join Cheat
Join 83294/s -- -97%
Cheat 2580687/s 2998% --
И код. После третьей строки, я думаю, вы поймете, почему это возможно обмануть:
use v5.14;
use Benchmark qw(cmpthese);
use Inline 'C';
sub an_join {
my ($s1, $s2) = @_;
return (join '', sort split(//, $s1)) eq
(join '', sort split(//, $s2));
}
use constant {
STR1 => 'abcdefghijklm',
STR2 => 'abcdefghijkmm',
STR3 => 'abcdefghijkml',
};
cmpthese(
0,
{
'Join' => 'an_join(STR1, STR2); an_join(STR1, STR3)',
'Cheat' => 'an_cheat(STR1, STR2); an_cheat(STR1, STR3)',
});
__END__
__C__
int an_cheat(const char *a, const char *b) {
unsigned char vec_a[26], vec_b[26];
const char *p, *end;
memset(vec_a, 0, sizeof(vec_a));
memset(vec_b, 0, sizeof(vec_b));
end = a+strlen(a);
for (p = a; p < end; ++p)
if (*p >= 'a' && *p <= 'z')
++vec_a[(*p)-'a'];
end = b+strlen(b);
for (p = b; p < end; ++p)
if (*p >= 'a' && *p <= 'z')
++vec_b[(*p)-'a'];
return 0 == memcmp(vec_a, vec_b, sizeof(vec_a));
}
Конечно, это обман, потому что он написан не на Perl, а на C. Кроме того, у него есть ограничения, которых нет у версии Perl (работает только с символами ASCII в нижнем регистре, которые являются наиболее значимыми - он просто игнорирует все остальное). Но если вам действительно нужна скорость, вы можете использовать обман таким образом.
редактирование:
Распространение на все Latin1 (ну, на самом деле, сырые 8-битные символы). Кроме того, я обнаружил, что компилятору удалось оптимизировать более простой цикл (без точечной арифметики), и его тоже легче читать, поэтому ... Бенчмарк говорит мне, что версия только для ASCII в нижнем регистре примерно на 10% быстрее:
int an_cheat_l1b(const char *a, const char *b) {
unsigned char vec_a[UCHAR_MAX], vec_b[UCHAR_MAX];
size_t len, i;
memset(vec_a, 0, sizeof(vec_a));
memset(vec_b, 0, sizeof(vec_b));
len = strlen(a);
for (i = 0; i < len; ++i)
++vec_a[((const unsigned char *)(a))[i]];
len = strlen(b);
for (i = 0; i < len; ++i)
++vec_b[((const unsigned char *)(b))[i]];
return 0 == memcmp(vec_a, vec_b, sizeof(vec_a));
}
Обратите внимание, что преимущество версии C увеличивается по мере того, как строка становится длиннее - что ожидается, поскольку ее Θ (n) в отличие от версий Perl O (n · logn). Также уменьшается штраф за полную Latin1, что означает, что штраф, вероятно, является memcmp.