Подтвердить что ты не робот

Поддерживает ли модификатор 'o' для регулярных выражений Perl какую-либо выгоду?

Раньше считалось полезным включить модификатор 'o' в конце регулярных выражений Perl. Текущая документация Perl даже не представляет ее, конечно, не на раздел модификаторов perlre.

Предоставляет ли он какую-либо пользу сейчас?

Он по-прежнему принимается по соображениям обратной совместимости, если ничего другого.


Как отмечено J A Faucett и brian d foy, модификатор 'o' все еще задокументирован, если вы найдете нужные места для поиска (одна из которых не является документацией perlre). Он упоминается на страницах perlop. Он также находится на страницах perlreref.

Как заметил Алан М в принятом ответе, лучшей современной техникой обычно является использование оператора qr//(цитируемого регулярного выражения).

4b9b3361

Ответ 1

Я уверен, что он все еще поддерживается, но он довольно устарел. Если вы хотите, чтобы регулярное выражение было скомпилировано только один раз, вам лучше использовать объект regex, например:

my $reg = qr/foo$bar/;

Интерполяция $bar выполняется, когда переменная инициализируется, поэтому вы всегда будете использовать кэшированное, скомпилированное регулярное выражение с включенным окружением. Но иногда вам нужно перекомпилировать регулярное выражение, потому что вы хотите, чтобы оно использовало переменную new value. Здесь пример Фридля используется в The Book:

sub CheckLogfileForToday()
{
  my $today = (qw<Sun Mon Tue Wed Thu Fri Sat>)[(localtime)[6]];

  my $today_regex = qr/^$today:/i; # compiles once per function call

  while (<LOGFILE>) {
    if ($_ =~ $today_regex) {
      ...
    }
  }
}

В рамках функции значение $today_regex остается неизменным. Но в следующий раз, когда вызывается функция, regex будет перекомпилирован с новым значением $today. Если он только что использовал

if ($_ =~ m/^$today:/io)

... регулярное выражение никогда не будет обновляться. Таким образом, с формой объекта у вас есть эффективность /o, не жертвуя гибкостью.

Ответ 2

Модификатор /o находится в документации perlop вместо perlre, поскольку это модификатор, похожий на цитату, а не модификатор regex. Это всегда казалось странным для меня, но так оно и есть. Начиная с Perl 5.20, он теперь указан в perlre просто для того, чтобы заметить, что вы, вероятно, не должны его использовать.

До Perl 5.6 Perl перекомпилировал регулярное выражение, даже если переменная не изменилась. Вам больше не нужно это делать. Вы можете использовать /o для компиляции регулярного выражения один раз, несмотря на дальнейшие изменения в переменной, но, как отмечали другие ответы, qr// для этого лучше.

Ответ 3

В документации Perl 5 версии 20.0 http://perldoc.perl.org/perlre.html он утверждает

Modifiers

Other Modifiers

…

o - pretend to optimize your code, but actually introduce bugs

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

Таким образом, вариант лучше всего избежать.

Ответ 4

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

Ответ 5

Здесь приведены тайминги для разных способов сопоставления вызовов.

$ perl -v | grep version
This is perl 5, version 20, subversion 1 (v5.20.1) built for x86_64-linux-gnu-thread-multi

$ perl const-in-re-once.pl | sort
0.200   =~ CONST
0.200   =~ m/$VAR/o
0.204   =~ m/literal-wo-vars/
0.252   =~ m,@{[ CONST ]},o
0.260   =~ $VAR
0.276   =~ m/$VAR/
0.336   =~ m,@{[ CONST ]},

Мой код:

#! /usr/bin/env perl

use strict;
use warnings;

use Time::HiRes qw/ tv_interval clock_gettime gettimeofday /;
use BSD::Resource qw/ getrusage RUSAGE_SELF /;

use constant RE =>
    qr{
        https?://
        (?:[^.]+-d-[^.]+\.)?
        (?:(?: (?:dev-)? nind[^.]* | mr02 )\.)?
        (?:(?:pda|m)\.)?
        (?:(?:news|haber)\.)
        (?:.+\.)?
        yandex\.
        .+
    }x;

use constant FINAL_RE => qr,^@{[ RE ]}(/|$),;

my $RE = RE;

use constant ITER_COUNT => 1e5;

use constant URL => 'http://news.trofimenkov.nerpa.yandex.ru/yandsearch?cl4url=www.forbes.ru%2Fnews%2F276745-visa-otklyuchila-rossiiskie-banki-v-krymu&lr=213&lang=ru';

timeit(
    '=~ m/literal-wo-vars/',
    ITER_COUNT,
    sub {
        for (my $i = 0; $i < ITER_COUNT; ++$i) {
            URL =~ m{
                ^https?://
                (?:[^.]+-d-[^.]+\.)?
                (?:(?: (?:dev-)? nind[^.]* | mr02 )\.)?
                (?:(?:pda|m)\.)?
                (?:(?:news|haber)\.)
                (?:.+\.)?
                yandex\.
                .+
                (/|$)
            }x
        }
    }
);

timeit(
    '=~ m/$VAR/',
    ITER_COUNT,
    sub {
        for (my $i = 0; $i < ITER_COUNT; ++$i) {
            URL =~ m,^$RE(/|$),
        }
    }
);

timeit(
    '=~ $VAR',
    ITER_COUNT,
    sub {
        my $r = qr,^$RE(/|$),o;
        for (my $i = 0; $i < ITER_COUNT; ++$i) {
            URL =~ $r
        }
    }
);

timeit(
    '=~ m/$VAR/o',
    ITER_COUNT,
    sub {
        for (my $i = 0; $i < ITER_COUNT; ++$i) {
            URL =~ m,^$RE(/|$),o
        }
    }
);

timeit(
    '=~ m,@{[ CONST ]},',
    ITER_COUNT,
    sub {
        for (my $i = 0; $i < ITER_COUNT; ++$i) {
            URL =~ m,^@{[ RE ]}(/|$),
        }
    }
);

timeit(
    '=~ m,@{[ CONST ]},o',
    ITER_COUNT,
    sub {
        for (my $i = 0; $i < ITER_COUNT; ++$i) {
            URL =~ m,^@{[ RE ]}(/|$),o
        }
    }
);

timeit(
    '=~ CONST',
    ITER_COUNT,
    sub {
        my $r = qr,^$RE(/|$),o;
        for (my $i = 0; $i < ITER_COUNT; ++$i) {
            URL =~ FINAL_RE
        }
    }
);

sub timeit {
    my ($name, $iters, $code) = @_;
    #my $t0 = [gettimeofday];
    my $t0 = (getrusage RUSAGE_SELF)[0];
    $code->();
    #my $el = tv_interval($t0);
    my $el = (getrusage RUSAGE_SELF)[0] - $t0;
    printf "%.3f\t%-17s\t%.9f\n", $el, $name, $el / $iters
}

Ответ 6

Одна вещь, которую, мистически, не делает, разрешает блок ONCE, по крайней мере, на 5.8.8.

perl -le 'for (1..3){ print; m/${\(print( "between 1 and 2 only"), 3)}/o and print "matched" }'