2009-02-15 45 views
38

在Perl正則表達式的末尾包含'o'修飾符被認爲是有益的。目前Perl documentation甚至似乎沒有列出它,當然不在modifiers section of perlrePerl正則表達式的'o'修飾符是否仍然提供任何好處?

它現在提供什麼好處嗎?

仍然接受,出於向後兼容的原因,如果沒有別的。


正如J A福塞特和布賴恩·d FOY指出的那樣,「O」修改仍記錄在案,如果你找到合適的地方可以去看看(其中一個是不是perlre文檔)。它在perlop頁面中提到。它也可在perlreref頁面找到。

正如Alan M在接受的答案中所指出的,更好的現代技術通常使用qr //(引用正則表達式)運算符。

+0

你提到並描述/ O選項,但只有在QR的//描述和M //運營商的頁面。 – 2009-02-15 05:10:53

+1

@J A Faucett:嗯,我沒有在該頁面上看到它,但可以在perlop(http://perldoc.perl.org/perlop.html#Regexp-Quote-Like-Operators)頁面中找到它。它沒有列在顯而易見的地方。我也發現它列在perlreref(http://perldoc.perl.org/perlreref.html)中。 – 2009-02-15 05:29:20

回答

37

我確定它仍然支持,但它已經過時了。如果你想在正則表達式只編譯一次,你最好使用一個正則表達式對象,像這樣:

my $reg = qr/foo$bar/; 

當變量初始化的$bar插值是這樣做,你將永遠被使用緩存,然後在封閉範圍內編譯正則表達式。但有時你想要正則表達式被重新編譯,因爲你想讓它使用變量的新值。這裏的弗裏德爾在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的值保持不變。但是下一次調用該函數時,將使用新值$today重新編譯正則表達式。如果他剛用過

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

...正則表達式永遠不會更新。因此,使用對象形式,您可以在不犧牲靈活性的情況下提高/ o的效率。

+0

我想我的$ reg = qw/foo $ bar /;應該是我的$ reg = qr/foo $ bar /; – gpojd 2009-02-15 04:44:11

+0

你可以通過點擊「編輯」鏈接編輯你的答案,你應該。 – 2009-02-15 05:13:19

4

這是在正則表達式包含變量引用的情況下的優化。它表明即使它中有一個變量,正則表達式也不會改變。這允許優化,否則將不可能。

17

/o修飾符位於perlop文檔中,而不是perlre文檔,因爲它是引用類修飾符而不是正則表達式修飾符。這對我來說一直很古怪,但事實就是如此。自從Perl 5.20以來,它現在列在perlre中,只是注意到你可能不應該使用它。

在Perl 5.6之前,即使變量沒有改變,Perl也會重新編譯正則表達式。你不需要那樣做了。儘管進一步更改了變量,但可以使用/o來編譯正則表達式,但正如其他答案指出的那樣,qr//更適合這一點。

6

在Perl 5的20版本中。0文檔 http://perldoc.perl.org/perlre.html 它指出

Modifiers 

Other Modifiers 

… 

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

這可能是說這是應該執行某種優化的一種幽默的方式,但實現破。

因此,該選項可能是最好的避免。

1

以下是調用匹配的不同方式的計時。

$ 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

一件事,mystifyingly,確實做的是,允許立即阻止,在5.8.8至少。

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