Эффективность разделения очень длинной транслитерации в Perl

У меня есть очень длинная транслитерация:

$text =~ tr/áàăâǎåǻäǟãȧǡąāȁȃɑʙƀɓƃćĉčċçȼƈɕʗďđðɖɗƌȡéèĕêěëėȩęēȅȇɇɛ/aaaaaaaaaaaaaaaaabbbbcccccccccdddddddeeeee/;
# Etc. (About 400 chars)

Я хочу разбить его на несколько транслитераций, так как получившийся код будет легче поддерживать:

$text =~ tr/áàăâǎåǻäǟãȧǡąāȁȃɑ/aaaaaaaaaaaaaaaaa/;
$text =~ tr/ʙƀɓƃ/bbbb/;
$text =~ tr/ćĉčċçȼƈɕʗ/ccccccccc/;
# Etc.

Я верю, что это замедлит ход событий, но я хотел бы знать наверняка. Этот процесс выполняется примерно 1000 раз в секунду на довольно загруженном сервере.

Спасибо.


person calvillo    schedule 06.11.2013    source источник
comment
tr и регулярное выражение - это две разные вещи. tr просто переводит символы, поэтому нет причин, по которым более длинный должен быть медленнее, чем короткий.   -  person acfrancis    schedule 06.11.2013
comment
несколько раз в секунду? используйте Benchmark, чтобы измерить время, которое на самом деле требуется, а затем решите, стоит ли вообще об этом думать.   -  person ysth    schedule 06.11.2013
comment
Прости за это. Я только что отредактировал вопрос. Терминология была неправильной, и я не очень ясно понимал свои цели.   -  person calvillo    schedule 06.11.2013


Ответы (3)


Вот эталон:

use Benchmark qw(:all);

my $str = 'áàăâǎåǻäǟãȧǡąāȁȃɑʙƀɓƃćĉčċçȼƈɕʗďđðɖɗƌȡéèĕêěëėȩęēȅȇɇɛ/aaaaaaaaaaaaaaaaabbbbcccccccccdddddddeeeee';
my $count = -2;
cmpthese($count, {
    'one tr' => sub {
        $str =~ tr/áàăâǎåǻäǟãȧǡąāȁȃɑʙƀɓƃćĉčċçȼƈɕʗďđðɖɗƌȡéèĕêěëėȩęēȅȇɇɛ/aaaaaaaaaaaaaaaaabbbbcccccccccdddddddeeeee/;
    },
    'multi tr' => sub {
        $str =~ tr/áàăâǎåǻäǟãȧǡąāȁȃɑ/aaaaaaaaaaaaaaaaa/;
        $str =~ tr/ʙƀɓƃ/bbbb/;
        $str =~ tr/ćĉčċçȼƈɕʗ/ccccccccc/;
        $str =~ tr/ďđðɖɗƌȡ/ddddddd/;
        $str =~ tr/éèĕêěëėȩęēȅȇɇɛ/eeeee/;
    },
});

результат:

              Rate multi tr   one tr
multi tr 1215538/s       --     -81%
one tr   6271883/s     416%       --

Как видим, один tr в 5 раз быстрее, чем multi-tr.

person Toto    schedule 06.11.2013

Вы можете создать транслитератор:

my %translits = (
   'áàăâǎåǻäǟãȧǡąāȁȃɑ' => 'a',
   'ʙƀɓƃ'              => 'b',
   'ćĉčċçȼƈɕʗ'         => 'c',
);

my $pat  = '';
my $repl = '';
for (keys(%translit)) {
   $pat  .= $_;
   $repl .= $translit{$_} x length($_);
}

my $tr1 = eval "sub { tr/\Q$pat\E/\Q$repl\E/ }" or die $@;
   -or-
my $tr2 = eval "sub { \$_[0] =~ tr/\Q$pat\E/\Q$repl\E/ }" or die $@;

Затем используйте его следующим образом:

$tr1->() for $str;
   -or-
$tr2->($str);

Конечно, вы всегда можете использовать Text::Unidecode.

person ikegami    schedule 06.11.2013
comment
Ваш код очень умный. Это решает мою проблему с обслуживанием, большое спасибо. Однако мне придется принять последний ответ, поскольку он более прямо отвечает на вопрос. Что касается Text::Unicode, я пробовал, но это работает таинственным образом, и в эти дни моя вера слаба. - person calvillo; 06.11.2013

Я ожидаю, что второе решение с тремя операциями будет медленнее, потому что оно повторно сканирует символы в $text, которые уже были заменены.

person user2719058    schedule 06.11.2013
comment
Неплохо подмечено. Каждый отдельный tr должен иметь одинаковую скорость независимо от того, сколько в нем символов. Но запуск его более одного раза должен быть медленнее. - person acfrancis; 06.11.2013
comment
@acfrancis: Почему вы думаете, что длина набора переводов и тип его элементов не имеют значения? Каждый входной символ должен быть сверен с каждым символом или классом символов в наборе перевода, поэтому я ожидаю, что размер последнего и тип его элементов будут иметь значение. - person user2719058; 06.11.2013
comment
Позвольте мне добавить, что я думаю об общем случае; с фиксированными символами в наборе перевода хэш может дать постоянную скорость, но с классами символов я не понимаю, как это сделать. И даже с хэшем, больший хэш требует большей вычислительной мощности для настройки... - person user2719058; 06.11.2013
comment
В пределах разумного я бы ожидал, что Perl реализует tr с чем-то вроде хеш-таблицы (каждый символ слева является ключом, а соответствующий символ справа является значением), которая может получить доступ к любому отдельному ключу в O (1) независимо от количество ключей. Если бы у вас был патологический tr с миллионами символов, это могло бы быть неправдой, но для повседневного использования, держу пари, это так. - person acfrancis; 06.11.2013
comment
Я пропустил этот второй комментарий, прежде чем я ответил. Разрешены ли классы символов в tr? Я думал, что их нет, но могу ошибаться. В любом случае, большая стоимость установки оплачивается во время компиляции, а не выполнения. - person acfrancis; 06.11.2013
comment
Тьфу, я перепутал tr в Perl с tr(1), извините за это. - person user2719058; 06.11.2013
comment
Если бы входной и выходной наборы не были различны, разделение tr могло бы даже изменить семантику по ошибке. Похоже, это не относится к кальвильо, но любой, кто читает этот вопрос, должен быть осторожен :) - person hobbs; 06.11.2013
comment
Похоже, пользователь 2719058 был прав. Я протестировал принятый ответ с образцом фактических данных, которые я обрабатываю, и результаты непротиворечивы: он увеличивается на 100% за каждую новую транслитерацию. Я попробую решение икегами. - person calvillo; 06.11.2013