Предоставляет ли модификатор 'o' для регулярных выражений Perl какие-либо преимущества? - PullRequest
38 голосов
/ 15 февраля 2009

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

Предоставляет ли это сейчас какую-либо выгоду?

Это по-прежнему принимается, из соображений обратной совместимости, если ничего больше.


Как отмечают J A Faucett и Брайан Д. Фой, модификатор 'o' все еще задокументирован, если вы найдете нужные места для поиска (одно из которых не является документацией perlre). Упоминается на страницах perlop . Это также можно найти на страницах perlreref .

Как отметил Алан М. в принятом ответе, более совершенный современный метод обычно заключается в использовании оператора qr // (quoted regex).

Ответы [ 6 ]

37 голосов
/ 15 февраля 2009

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

my $reg = qr/foo$bar/;

Интерполяция $bar выполняется при инициализации переменной, поэтому с этого момента вы всегда будете использовать кешированное скомпилированное регулярное выражение в пределах области действия. Но иногда вы хотите перекомпилировать регулярное выражение, потому что вы хотите, чтобы оно использовало новое значение переменной. Вот пример, который Фридль использовал в Книге :

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 остается неизменным. Но при следующем вызове функции регулярное выражение будет перекомпилировано с новым значением $today. Если бы он только что использовал

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

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

17 голосов
/ 15 февраля 2009

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

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

6 голосов
/ 23 сентября 2014

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

Modifiers

Other Modifiers

…

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

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

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

4 голосов
/ 15 февраля 2009

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

2 голосов
/ 26 декабря 2014

Ниже приведены настройки времени для различных способов сопоставления вызовов.

$ 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
}
0 голосов
/ 23 марта 2015

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

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...