Как вы заставляете Perl перекомпилировать регулярное выражение, скомпилированное с "/ o" по требованию? - PullRequest
7 голосов
/ 01 июня 2011

Технический вопрос:

С учетом регулярного выражения:

my $regEx = qr{whatever$myVar}oxi; # Notice /o for "compile-once"

Какой самый эффективный способ заставить его перекомпилировать по требованию ?(например, когда я знаю из логики программы, что значение $myVar изменилось) без удаления /o и в зависимости от внутренних умений Perl для автоматической перекомпиляции?

ПРИМЕЧАНИЕ. Регулярное выражение используется в подстановке, которая можетвлияет на правила перекомпиляции sans / o:

$string2 =~ s/$regEx//;

Контекст:

  • У меня есть регулярное выражение, построенное с помощью slurpingв довольно длинной (> 1 КБ) строке из файла конфигурации.

    • Этот файл перечитывается один раз каждые 60 минут.

    • Если строка, считанная из файла, изменится (как определено путем изменения временной метки файла), я хочу перекомпилировать регулярное выражение, используя значение строки с измененной формойв $myVar.

  • Регулярное выражение используется многократно и часто в модуле Perl, работающем под mod_perl.

    • Это означает, что (в сочетании со строкой длиной> 1-2 КБ) Я должен использовать модификатор "/o" , чтобы принудительно выполнить однократную компиляцию в регулярном выражении, чтобы избежать снижения производительности Perl, повторно проверяющего, является ли переменнаязначение изменилось (эта эвристика имеет значение perlop qr//, поскольку регулярное выражение используется как часть s///, как показано выше, а не само по себе как совпадение).

    • Это, в свою очередь, означает, что, когда я узнаю, что переменная изменилась после повторного использования ее в течение 1 часа, мне нужно принудительно пересобрать регулярное выражение, несмотря на модификатор /o.

ОБНОВЛЕНИЕ: Вот иллюстрация того, почему мне нужно /o - без него, регex перекомпилируется (и, следовательно, обязательно проверяется) на каждой итерации цикла;с ним это НЕ:

$ perl -e '{for (my $i=0; $i<3; $i++) {
                 my $re = qr{$i}oix; $s="123"; $s =~ s/$re//; 
                 print "i=$i; s=$s\n"; }}'
i=0; s=123
i=1; s=123
i=2; s=123

$ perl -e '{ for (my $i=0; $i<3; $i++) { 
                  my $re = qr{$i}ix; $s="123"; $s =~ s/$re//; 
                  print "i=$i; s=$s\n"; }}'
i=0; s=123
i=1; s=23
i=2; s=13

Ответы [ 3 ]

4 голосов
/ 02 июня 2011
когда я знаю из логики программы, что значение $ myVar изменилось

m//, s/// и qr// only compile, если шаблон не изменяется.Все, что вам нужно сделать, чтобы получить запрашиваемое вами поведение, - это удалить /o.

$ perl -Mre=debug -e'
    qr/$_/ for qw( abc abc def def abc abc );
' 2>&1 | grep Compiling
Compiling REx "abc"
Compiling REx "def"
Compiling REx "abc"

Следовательно,

Если строка, считанная из файла, изменяется (как определено при изменении файлаотметка времени), я хочу перекомпилировать регулярное выражение, используя значение строки с измененной формой в $ myVar.
my $new_myVar = ...;
if ($myVar ne $new_myVar) {
   $re = qr/$new_myVar/;
   $myVar = $new_myVar;
}
...
s/$re/.../

или просто

$myVar = ...;
...
s/$myVar/.../
3 голосов
/ 01 июня 2011

Вы в основном ответили на свой вопрос.Используйте qr{...} для создания скомпилированного объекта регулярного выражения и затем используйте его:

my $re = qr{...};

...

if ($str =~ $re) {
   # this used the statically compiled object
}

...

if ($time_to_recompile) {
    $re = qr{...};
}

Вам даже не нужен модификатор "/ o".

2 голосов
/ 02 июня 2011

По данным perlop

Эффект модификатора 'o' не распространяться, ограничиваясь тем шаблоны, явно использующие его.

Так что, если вы напишите

my $str = 'x';
my $re  = qr/$str/o;
...
if (s/$re//) {
    ...
}

Perl по-прежнему будет проверять, изменился ли $re при выполнении s///. /o действует как обещание, что значение $str, использованное при компиляции $re, не изменится, поэтому, если вы повторно выполните qr//, вы получите тот же результат , даже если $str изменилось. Вы можете увидеть это с use re 'debug':

use strict;
use warnings;
use re 'debug';

foreach my $i (0 .. 2) {
    my $s  = '123';

    print STDERR "Setting \$re\n";
    my $re = qr/$i/o;

    print STDERR "Performing s///\n";
    $s =~ s/$re//; 
}

С модификатором /o вы увидите «Compiling REx ...» только после «Setting $ re» в первый раз в цикле. Без этого вы будете видеть это каждую итерацию.

Вывод: если вы хотите изменить шаблон во время выполнения, вам не следует использовать /o. Это не повлияет на s/// и не позволит вам перекомпилировать $re, когда вам нужно.

...