Los paréntesis especiales:
(??{ Código Perl })hacen que el
Código Perl
sea evaluado durante el tiempo de matching.
El resultado de la evaluación se trata como una expresión regular. El match continuará
intentando casar con la expresión regular retornada.
Los paréntesis en la expresión regular retornada no cuentan en el patrón exterior. Véase el siguiente ejemplo:
pl@nereida:~/Lperltesting$ cat -n postponedregexp.pl 1 #!/usr/local/lib/perl/5.10.1/bin//perl5.10.1 -w 2 use v5.10; 3 use strict; 4 5 my $r = qr{(?x) # ignore spaces 6 ([ab]) # save 'a' or 'b' in $1 7 (??{ "($^N)"x3 }) # 3 more of the same as in $1 8 }; 9 say "<$&> lastpar = $#-" if 'bbbb' =~ $r; 10 say "<$&> lastpar = $#-" if 'aaaa' =~ $r; 11 say "<abab> didn't match" unless 'abab' =~ $r; 12 say "<aaab> didn't match" unless 'aaab' =~ $r;
Como se ve, hemos accedido desde el código interior
al último paréntesis usando $^N
.
Sigue una ejecución:
pl@nereida:~/Lperltesting$ ./postponedregexp.pl <bbbb> lastpar = 1 <aaaa> lastpar = 1 <abab> didn't match <aaab> didn't match
Consideremos el problema de escribir una expresión regular que reconoce secuencias no vacías de dígitos tales que la longitud de la secuencia restante viene determinada por el primer dígito. Esta es una solución:
pl@nereida:~/Lperltesting$ cat -n intints.pl 1 #!/usr/local/lib/perl/5.10.1/bin//perl5.10.1 -w 2 use v5.10; 3 use strict; 4 5 my $r = qr{(?x) # ignore spaces 6 (\d) # a digit 7 ( (??{ 8 "\\d{$^N}" # as many as the former 9 }) # digit says 10 ) 11 }; 12 say "<$&> <$1> <$2>" if '3428' =~ $r; 13 say "<$&> <$1> <$2>" if '228' =~ $r; 14 say "<$&> <$1> <$2>" if '14' =~ $r; 15 say "24 does not match" unless '24' =~ $r; 16 say "4324 does not match" unless '4324' =~ $r;
Cuando se ejecuta se obtiene:
pl@nereida:~/Lperltesting$ ./intints.pl <3428> <3> <428> <228> <2> <28> <14> <1> <4> 24 does not match 4324 does not match
Otro ejemplo: queremos escribir una expresión regular que reconozca secuencias
de $n
dígitos en las que no todos los dígitos se repiten. Donde quizá $n
es capturado
de un paréntesis anterior en la expresión regular. Para simplificar la ilustración
de la técnica supongamos que $n = 7
:
pl@nereida:~$ perl5.10.1 -wdE 0 main::(-e:1): 0 DB<1> x join '', map { "(?!".$_."{7})" } 0..9 0 '(?!0{7})(?!1{7})(?!2{7})(?!3{7})(?!4{7})(?!5{7})(?!6{7})(?!7{7})(?!8{7})(?!9{7})' DB<2> x '7777777' =~ /(??{join '', map { "(?!".$_."{7})" } 0..9})(\d{7})/ empty array DB<3> x '7777778' =~ /(??{join '', map { "(?!".$_."{7})" } 0..9})(\d{7})/ 0 7777778 DB<4> x '4444444' =~ /(??{join '', map { "(?!".$_."{7})" } 0..9})(\d{7})/ empty array DB<5> x '4422444' =~ /(??{join '', map { "(?!".$_."{7})" } 0..9})(\d{7})/ 0 4422444
Se trata en este ejercicio de generalizar la expresión regular introducida en la sección 3.2.5 para reconocer los palabra-palíndromos.
Se trata de encontrar una regexp que acepte que la lectura derecha e inversa de una frase en Español pueda diferir en la acentuación (como es el caso del clásico palíndromo dábale arroz a la zorra el abad). Una solución trivial es preprocesar la cadena eliminando los acentos. Supondremos sin embargo que se quiere trabajar sobre la cadena original. He aquí una solucion:
1 pl@nereida:~/Lperltesting$ cat actionspanishpalin.pl 2 #!/usr/local/lib/perl/5.10.1/bin//perl5.10.1 -w -CIOEioA 3 use v5.10; 4 use strict; 5 use utf8; 6 use re 'eval'; 7 use Switch; 8 9 sub f { 10 my $char = shift; 11 12 switch($char) { 13 case [ qw{a á} ] { return '[aá]' } 14 case [ qw{e é} ] { return '[eé]' } 15 case [ qw{i í} ] { return '[ií]' } 16 case [ qw{o ó} ] { return '[oó]' } 17 case [ qw{u ú} ] { return '[uú]' } 18 else { return $char }; 19 } 20 } 21 22 my $regexp = qr/^(\W* (?: 23 (\w) (?-2)(??{ f($^N) }) 24 | \w? 25 ) \W* 26 ) 27 $ 28 /ix; 29 30 my $input = <>; # Try: 'dábale arroz a la zorra el abad'; 31 chomp($input); 32 if ($input =~ $regexp) { 33 say "$input is a palindrome"; 34 } 35 else { 36 say "$input does not match"; 37 }
Sigue un ejemplo de ejecución:
pl@nereida:~/Lperltesting$ ./actionspanishpalin.pl dábale arroz a la zorra el abad dábale arroz a la zorra el abad is a palindrome pl@nereida:~/Lperltesting$ ./actionspanishpalin.pl éoíúaáuioé éoíúaáuioé is a palindrome pl@nereida:~/Lperltesting$ ./actionspanishpalin.pl dáed dáed does not match
Véase el nodo Complex regex for maths formulas para la formulación del problema:
Hiya monks,
Im having trouble getting my head around a regular expression to match sequences. I need to catch all exceptions where a mathematical expression is illegal...
There must be either a letter or a digit either side of an operator
parenthesis must open and close next to letters or digits, not next to operators, and do not have to exist
variables must not be more than one letter
Nothing other than a-z,A-Z,0-9,+,-,*,/,(,)
can be used
Can anyone offer a hand on how best to tackle this problem?
many thanks
La respuesta dada por ikegami
usa (?{{ ... })
para conseguir una conducta recursiva en versiones de perl anteriores a la 5.10:
pl@nereida:~/Lperltesting$ cat -n complexformula.pl 1 #!/usr/bin/perl 2 use strict; 3 use warnings; 4 5 sub is_valid_expr { 6 use re 'eval'; # to allow Eval-group at runtime 7 8 local our ($skip, $term, $expr); 9 $skip = qr! \s* !x; 10 $term = qr! $skip [a-zA-Z]+ # A term is an identifier 11 | $skip [1-9][0-9]* # or a number 12 | $skip \( (??{ $expr }) $skip # or an expression 13 \) 14 !x; 15 $expr = qr! $term # A expr is a term 16 (?: $skip [-+*/] $term )* # or a term + a term ... 17 !x; 18 19 return $_[0] =~ / ^ $expr $skip \z /x; 20 } 21 22 print(is_valid_expr($_) ? "$_ is valid\n" : "$_ is not valid\n") foreach ( 23 '(a + 3)', 24 '(3 * 4)+(b + x)', 25 '(5 - a)*z', 26 '3 + 2', 27 28 '!3 + 2', 29 '3 + 2!', 30 31 '3 a', 32 '3 3', 33 '3 * * 3', 34 35 '2 - 3 * 4', 36 '2 - 3 + 4', 37 );
Sigue el resultado de la ejecución:
pl@nereida:~/Lperltesting$ perl complexformula.pl (a + 3) is valid (3 * 4)+(b + x) is valid (5 - a)*z is valid 3 + 2 is valid !3 + 2 is not valid 3 + 2! is not valid 3 a is not valid 3 3 is not valid 3 * * 3 is not valid 2 - 3 * 4 is valid 2 - 3 + 4 is valid
Estos son algunos puntos a tener en cuenta cuando se usan patrones postpuestos.
Véase la entrada (??{ code })
en la sección 'Extended-Patterns' en perlre:
WARNING: This extended regular expression feature is considered experimental, and may be changed without notice. Code executed that has side effects may not perform identically from version to version due to the effect of future optimisations in the regex engine.
This is a postponed regular subexpression. The code is evaluated at run time, at the moment this subexpression may match. The result of evaluation is considered as a regular expression and matched as if it were inserted instead of this construct.
The code is not interpolated.
As before, the rules to determine where the code ends are currently somewhat convoluted.
Because perl's regex engine is not currently re-entrant, delayed code may not invoke the regex engine either directly withm//
ors///
), or indirectly with functions such as split.
Recursing deeper than 50 times without consuming any input string will result in a fatal error. The maximum depth is compiled into perl, so changing it requires a custom build.