2009-02-15 6 views
38

Solía ​​considerarse beneficioso incluir el modificador 'o' al final de las expresiones regulares de Perl. El actual Perl documentation ni siquiera parece enumerarlo, ciertamente no en el modifiers section of perlre.¿El modificador "o" para las expresiones regulares de Perl aún proporciona algún beneficio?

¿Ofrece algún beneficio ahora?

Es es todavía aceptado, por razones de compatibilidad con versiones anteriores, nada más.


Como señaló J Un Faucett y Brian D Foy, el modificador 'o' todavía está documentado, si encuentras los lugares adecuados para buscar (una de las cuales no es la documentación perlre). Se menciona en las páginas perlop. También se encuentra en las páginas perlreref.

Como señala Alan M en la respuesta aceptada, la mejor técnica moderna suele ser utilizar el operador qr // (regex entre comillas).

+0

La página que mencionas Cómo describe la opción/o, pero sólo en las descripciones de QR // M // y operadores. –

+1

@J A Faucett: Hmm, no lo veo en esa página, pero puedo encontrarlo mencionado en las páginas de perlop (http://perldoc.perl.org/perlop.html#Regexp-Quote-Like-Operators). No estaba en la lista en el lugar obvio. También lo encontré listado en el perlreref (http://perldoc.perl.org/perlreref.html). –

Respuesta

37

Estoy seguro de que todavía es compatible, pero es bastante obsoleto. Si desea que la expresión regular para ser compilado una sola vez, es mejor utilizar un objeto de expresiones regulares, así:

my $reg = qr/foo$bar/; 

La interpolación de $bar se realiza cuando se inicializa la variable, por lo que siempre va a utilizar el Regex compilado en caché a partir de ese momento dentro del alcance adjunto. Pero a veces usted quiere la expresión regular que se recompilará, porque quiere que use el nuevo valor de la variable. Aquí está el ejemplo Friedl utiliza en 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) { 
     ... 
    } 
    } 
} 

Dentro del ámbito de la función, el valor de $ today_regex sigue siendo el mismo. Pero la próxima vez que se llame a la función, la expresión regular se volverá a compilar con el nuevo valor de $today. Si acabara de utilizar

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

... la expresión regular nunca se actualizará. Entonces, con la forma de objeto, tiene la eficiencia de/o sin sacrificar la flexibilidad.

+0

Creo que mi $ reg = qw/foo $ bar /; debería ser my $ reg = qr/foo $ bar /; – gpojd

+0

Puede editar su respuesta haciendo clic en el enlace "editar", y usted debería. –

+0

Sí, solo corrí aquí para arreglar eso, pero Jonathan me ganó. Gracias a todos. –

4

Esta es una optimización en el caso de que la expresión regular incluya una referencia de variable. Indica que la expresión regular no cambia aunque tenga una variable dentro. Esto permite optimizaciones que de otro modo no serían posibles.

17

El modificador /o es en la documentación perlop en lugar de la documentación perlre ya que es un modificador de la cotización en lugar de como un modificador de expresiones regulares. Eso siempre me ha parecido extraño, pero así son las cosas. Desde Perl 5.20, ahora aparece en perlre simplemente para notar que probablemente no deberías usarlo.

Antes de Perl 5.6, Perl recompilaría la expresión regular incluso si la variable no hubiera cambiado. No necesitas hacer eso nunca más. Puede usar /o para compilar la expresión regular una vez a pesar de los cambios adicionales en la variable, pero como se señaló en las otras respuestas, qr// es mejor para eso.

6

En la versión 20 de Perl 5.0 documentación http://perldoc.perl.org/perlre.html afirma

Modifiers 

Other Modifiers 

… 

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

que puede ser una forma divertida de decir que se suponía que realizar algún tipo de optimización, pero la implementación se ha roto.

Por lo tanto, la mejor opción es evitarla.

+0

La base técnica es que m // o se comporta de manera diferente bajo perls roscados y no roscados. Con perls roscados m/$ foo/o no vuelve a compilar la expresión regular con un nuevo valor de $ foo, pero sin hilos lo hace. Esto generalmente se considera un error. Hasta el 5.005_02, podría cambiar $ foo en tiempo de compilación, pero no en el tiempo de ejecución, lo que es más consistente (OPpRUNTIME). Pero esto nunca se solucionó, por lo que ahora m/$ foo/o se considera defectuoso con perls roscados. con v5.26, el indicador OPpRUNTIME se eliminó por completo sin explicar el fondo del problema. – rurban

1

Aquí se muestran los tiempos de las diferentes formas de igualar las llamadas.

$ 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 ]}, 

Mi código:

#! /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 
} 
+1

Bienvenido a Stack Overflow. Por favor, lea la página [Acerca de]. Gracias por correr estos tiempos. Los primeros tres resultados probablemente sean esencialmente los mismos; los segundos tres también podrían ser "lo mismo", o podría haber alguna gradación en los tiempos; el último resultado parece estar separado. ¿Puedes incluir el código que usaste en tus pruebas para que pueda entender más exactamente lo que mediste? Si había un archivo de datos, no debería incluirlo, pero ayudaría a identificar qué tan grande era el archivo: el número de líneas y el número total de bytes en él. –

0

Una cosa que, mystifyingly, hace no hacer es, permitir que un VEZ bloque, por lo menos en 5.8.8.

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

Cuestiones relacionadas