Sin conocer el motor de expresiones regulares, ni siquiera se puede decidir si esos son correctos .
Por ejemplo, un POSIX ERE es de más larga más a la izquierda, no más a la izquierda más larga, para que elija el más largo de una serie de alternativas, y por lo tanto elegir una cadena a juego "ab"
contra /a|ab/
coincidirá con la cadena completa, "ab"
. Pero un NFA de retroceso normal como lo ve más a menudo haría algo más allí: le importaría ordenar, por lo que emparejar la misma cadena "ab"
con el mismo patrón /a|ab/
seleccionaría solo la parte inicial, "a"
.
La siguiente pregunta es el grupo de captura en el mismo patrón. Si son intencionales, son extraños, ya que mantiene números de dos dígitos pero no números de un dígito. Los otros patrones no hacen eso, sin embargo, se dice que son idénticos en el comportamiento. Así que voy a suponer que tienen un error aquí. De lo contrario, el uso de la memoria del grupo de captura, por supuesto, costará más para arrancar de lo que tomaría , no para hacerlo.
El siguiente problema es la falta de anclajes de ningún tipo. Nuevamente, no podemos saber si son correctos, porque no está claro cómo se vería el conjunto de entrada ni qué hace ese motor en particular con patrones sin anclaje. La mayoría de los motores buscarán en todas partes de la cadena, pero algunos de los menos amigables con los programadores agregarán "de manera útil" anclas de inicio de línea (BOL) y fin de línea (EOL) allí. En motores más habituales, donde esto no ocurre, un código postal en el medio de la línea también coincidiría, ya que cinco dígitos obviamente contienen subcadenas de uno y dos dígitos. Si desea anclas ^
y $
, o anclajes \b
, no puedo adivinar.
Así que tengo que hacer algunas conjeturas aquí. Voy a dejar los anclajes, pero voy a reordenar la rama de las tres versiones, porque de lo contrario nunca se puede hacer coincidir un número de dos dígitos con los tipos normales (no POSIX) de NFA de retroceso. La mayoría de las cosas se ejecutan.
Antes de siquiera considerar el tiempo, realmente puede pagar para ver qué tipo de programa construye el compilador de expresiones regulares a partir de esos patrones.
% perl -Mre=debug -ce '@pats = (qr/[0-9]{1,2}/, qr/[0-9]?[0-9]/, qr/[0-9][0-9]|[0-9]/)'
Compiling REx "[0-9]{1,2}"
Final program:
1: CURLY {1,2} (14)
3: ANYOF[0-9][] (0)
14: END (0)
stclass ANYOF[0-9][] minlen 1
Compiling REx "[0-9]?[0-9]"
synthetic stclass "ANYOF[0-9][]".
Final program:
1: CURLY {0,1} (14)
3: ANYOF[0-9][] (0)
14: ANYOF[0-9][] (25)
25: END (0)
stclass ANYOF[0-9][] minlen 1
Compiling REx "[0-9][0-9]|[0-9]"
Final program:
1: BRANCH (24)
2: ANYOF[0-9][] (13)
13: ANYOF[0-9][] (36)
24: BRANCH (FAIL)
25: ANYOF[0-9][] (36)
36: END (0)
minlen 1
-e syntax OK
Freeing REx: "[0-9]{1,2}"
Freeing REx: "[0-9]?[0-9]"
Freeing REx: "[0-9][0-9]|[0-9]"
Realmente es una buena idea mirar los patrones compilados. Puede ser aún más instructivo observar el patrón compilado que se está ejecutando. Aquí vamos a ver tanto:
% perl -Mre=debug -e '"aabbbababbaaqcccaaaabcacabba" =~ /abc|bca|cab|caab|baac|bab|aaa|bbb/'
Compiling REx "abc|bca|cab|caab|baac|bab|aaa|bbb"
Final program:
1: TRIEC-EXACT[abc] (25)
<abc>
<bca>
<cab>
<caab>
<baac>
<bab>
<aaa>
<bbb>
25: END (0)
stclass AHOCORASICKC-EXACT[abc] minlen 3
Matching REx "abc|bca|cab|caab|baac|bab|aaa|bbb" against "aabbbababbaaqcccaaaabcacabba"
Matching stclass AHOCORASICKC-EXACT[abc] against "aabbbababbaaqcccaaaabcacabba" (28 chars)
0 <> <aabbbababb> | Charid: 1 CP: 61 State: 1, word=0 - legal
1 <a> <abbbababba> | Charid: 1 CP: 61 State: 2, word=0 - legal
2 <aa> <bbbababbaa> | Charid: 2 CP: 62 State: 11, word=0 - fail
2 <aa> <bbbababbaa> | Fail transition to State: 2, word=0 - legal
3 <aab> <bbababbaaq> | Charid: 2 CP: 62 State: 3, word=0 - fail
3 <aab> <bbababbaaq> | Fail transition to State: 5, word=0 - legal
4 <aabb> <bababbaaqc> | Charid: 2 CP: 62 State: 13, word=0 - legal
5 <aabbb> <ababbaaqcc> | Charid: 1 CP: 61 State: 14, word=8 - accepting
Matches word #8 at position 2. Trying full pattern...
2 <aa> <bbbababbaa> | 1:TRIEC-EXACT[abc](25)
2 <aa> <bbbababbaa> | State: 1 Accepted: 0 Charid: 2 CP: 62 After State: 5
3 <aab> <bbababbaaq> | State: 5 Accepted: 0 Charid: 2 CP: 62 After State: 13
4 <aabb> <bababbaaqc> | State: 13 Accepted: 0 Charid: 2 CP: 62 After State: 14
5 <aabbb> <ababbaaqcc> | State: 14 Accepted: 1 Charid: 8 CP: 0 After State: 0
got 1 possible matches
only one match left: #8 <bbb>
5 <aabbb> <ababbaaqcc> | 25:END(0)
Match successful!
Freeing REx: "abc|bca|cab|caab|baac|bab|aaa|bbb"
aquí el compilador tiene realmente inteligente sobre nosotros, y que se compilan en una estructura trie-Aho Corasick. Obviamente, esto va a funcionar de manera bastante diferente a como funcionaría un NFA retroactivo normal en el mismo programa.
De todos modos, aquí están los tiempos para sus patrones, o cerca de ellos. Agregué una formulación alternativa para el número dos, y cambié el orden de las alternativas en el número tres.
testing against short_fail
Rate second first third second_alt
second 9488823/s -- -9% -21% -29%
first 10475308/s 10% -- -13% -22%
third 11998438/s 26% 15% -- -11%
second_alt 13434377/s 42% 28% 12% --
testing against long_fail
Rate second first third second_alt
second 11221411/s -- -3% -5% -5%
first 11618967/s 4% -- -1% -1%
third 11776451/s 5% 1% -- -0%
second_alt 11786700/s 5% 1% 0% --
testing against short_pass
Rate first second_alt second third
first 11720379/s -- -4% -7% -7%
second_alt 12199048/s 4% -- -3% -4%
second 12593191/s 7% 3% -- -1%
third 12663378/s 8% 4% 1% --
testing against long_pass
Rate third second first second_alt
third 11135053/s -- -1% -5% -8%
second 11221655/s 1% -- -4% -7%
first 11716924/s 5% 4% -- -3%
second_alt 12042240/s 8% 7% 3% --
Eso fue producto de este programa:
#!/usr/bin/env perl
use Benchmark qw<cmpthese>;
$short_fail = "a" x 1;
$long_fail = "a" x 600;
$short_pass = $short_fail . 42;
$long_pass = $long_fail . 42;
for my $name (qw< short_fail long_fail short_pass long_pass >) {
print "\ntesting against $name\n";
$_ = $$name;
cmpthese 0 => {
first => '/[0-9]{1,2}/',
second => '/[0-9]?[0-9]/',
second_alt => '/[0-9][0-9]?/',
third => '/[0-9][0-9]|[0-9]/',
}
}
Aquí están los números con anclajes añadido:
testing against short_fail
Rate first second second_alt third
first 11720380/s -- -3% -4% -11%
second 12058622/s 3% -- -1% -9%
second_alt 12180583/s 4% 1% -- -8%
third 13217006/s 13% 10% 9% --
testing against long_fail
Rate third first second_alt second
third 11378120/s -- -2% -4% -12%
first 11566419/s 2% -- -2% -10%
second_alt 11830740/s 4% 2% -- -8%
second 12860517/s 13% 11% 9% --
testing against short_pass
Rate second third second_alt first
second 11540465/s -- -5% -5% -7%
third 12093336/s 5% -- -0% -3%
second_alt 12118504/s 5% 0% -- -2%
first 12410348/s 8% 3% 2% --
testing against long_pass
Rate first second second_alt third
first 11423466/s -- -1% -4% -7%
second 11545540/s 1% -- -3% -7%
second_alt 11870086/s 4% 3% -- -4%
third 12348377/s 8% 7% 4% --
Y aquí está el programa mínimamente modificado que produjo el segundo conjunto de números:
#!/usr/bin/env perl
use Benchmark qw<cmpthese>;
$short_fail = 1 . "a";
$long_fail = 1 . "a" x 600;
$short_pass = 2;
$long_pass = 42;
for my $name (qw< short_fail long_fail short_pass long_pass >) {
print "testing against $name\n";
$_ = $$name;
cmpthese 0 => {
first => '/^(?:[0-9]{1,2})$/',
second => '/^(?:[0-9]?[0-9])$/',
second_alt => '/^(?:[0-9][0-9]?)$/',
third => '/^(?:[0-9][0-9]|[0-9])$/',
}
}
que dependerá de la implementación de expresiones regulares que se utiliza. – ocodo
No debe ser demasiado pedante, pero todos coinciden más de 0-99. También coinciden con 00-09, que posiblemente NO sean valores que quiera unir. – dietbuddha
@dietbuddha, tal vez deberíamos ofrecerle _you_ el trabajo :-) Buena captura. – paxdiablo