El problema es encontrar los códigos que comienzan por 12. En negrita
se han resaltado las soluciones. Son soluciones sólo aquellas que, comienzan
por 12
en una posición múltiplo de seis.
Una solución es:
@nums = grep {m/^12/} m/\d{6}/g;
que genera una lista con los números y luego selecciona los que comienzan por
12
.
Otra solución es:
@nums = grep { defined } m/(12\d{4})|\d{6}/g;
que aprovecha que la expresión regular devolverá una lista vacía cuando
el número no empieza por 12
:
DB<1> $x = '012345678901123334234567890123125934890123345126' DB<2> x ($x =~ m/(12\d{4})|\d{6}/g) 0 undef 1 undef 2 123334 3 undef 4 undef 5 125934 6 undef 7 undefObsérvese que se esta utilizando también que el operador
|
no es greedy.
¿Se puede resolver el problema usando sólamente una expresión regular? Obsérvese que esta solución ``casi funciona'':
DB<3> x @nums = $x =~ m/(?:\d{6})*?(12\d{4})/g; 0 123334 1 125934 2 123345
recoge la secuencia mas corta de grupos de seis dígitos que no casan, seguida
de una secuencia que casa.
El problema que tiene esta solución
es al final, cuando se han casado todas las soluciones, entonces
la búsqueda exhaustiva hará que nos muestre soluciones que no comienzan en posiciones
múltiplo de seis. Por eso encuentra 123345
:
@nums = m/(?:\d{6})*?(12\d{4})(?:(?!12)\d{6})*/g;
Se asume que existe al menos un éxito en la entrada inicial. Que es un extraordinario ejemplo de como el uso de paréntesis de agrupamiento simplifica y mejora la legibilidad de la solución. Es fantástico también el uso del operador de predicción negativo.
El ancla \G
ha sido concebida para su uso con la opción /g
.
Casa con el punto en la cadena en el que terminó el último emparejamiento.
Cuando se trata del primer intento o no se está usando /g
, usar \G
es lo mismo que usar \A
.
Mediante el uso de este ancla es posible formular la siguiente solución al problema planteado:
pl@nereida:~/Lperltesting$ perl -wde 0 main::(-e:1): 0 DB<1> $_ = '012345678901123334234567890123125934890123345126' DB<2> x m/\G(?:\d{6})*?(12\d{4})/g 0 123334 1 125934
Si lo que se quiere es sustituir las secuencias deseadas es poisble hacerlo con la siguiente expresión regular:
casiano@nereida:~/docs/curriculums/CV_MEC$ perl -wde 0 DB<1> x $x = '012345678901123334234567890123125934890123345126' 0 012345678901123334234567890123125934890123345126 DB<2> x ($y = $x) =~ s/(12\d{4})|\d{6}/$1? "-$1-":$& /ge 0 8 DB<3> p $y 012345678901-123334-234567890123-125934-890123345126
HTML
.
1 #!/usr/bin/perl -w 2 use strict; 3 use Term::ANSIScreen qw/:constants/; 4 5 my $bold = BOLD(); 6 my $clear = CLEAR(); 7 my $line = 1; 8 9 # read paragraph 10 local $/ = ".\n"; 11 while (my $par = <>) { 12 next unless $par =~ s{ 13 \b # start word ... 14 ([a-z]+) # grab word in $1 and \1 15 ( # save the tags and spaces in $2 16 (\s|<[^>]+>)+ # spaces or HTML tags 17 ) 18 (\1\b) # repeated word in $4 19 }!$bold$1$clear$2$bold$4$clear!igx; 20 21 $par =~ s/^/"$ARGV(".$line++."): "/meg; # insert filename and line number 22 23 print $par; 24 }
Sigue un ejemplo de uso:
pl@nereida:~/Lperltesting$ cat -n t.t 1 one one 2 nothing rep 3 is two three 4 three four 5 pl@nereida:~/Lperltesting$ ./repeatedwords2.pl t.t t.t(1): one one t.t(2): nothing rep t.t(3): is two three t.t(4): three four t.t(5):
$text
proveniente
de un fichero CSV
(Comma Separated Values). Esto es
el fichero contiene líneas con el formato:
"earth",1,"moon",9.374
Esta línea representa cinco campos.
Es razonable querer guardar esta información en un array,
digamos @field
, de manera que $field[0] == 'earth'
,
$field[1] == '1'
, etc.
Esto no sólo implica descomponer la cadena en campos sino
también quitar las comillas de los campos entrecomillados.
La primera solución que se nos ocurre es hacer uso de la
función split
:
@fields = split(/,/,$text);
Pero esta solución deja las comillas dobles en los campos
entrecomillados. Peor aún, los campos entrecomillados pueden contener
comas, en cuyo caso la división proporcionada por split
sería
errónea.
1 #!/usr/bin/perl -w 2 use Text::ParseWords; 3 4 sub parse_csv { 5 my $text = shift; 6 my @fields = (); # initialize @fields to be empty 7 8 while ($text =~ 9 m/"(([^"\\]|\\.)*)",? # quoted fields 10 | 11 ([^,]+),? # $3 = non quoted fields 12 | 13 , # allows empty fields 14 /gx 15 ) 16 { 17 push(@fields, defined($1)? $1:$3); # add the just matched field 18 } 19 push(@fields, undef) if $text =~ m/,$/; #account for an empty last field 20 return @fields; 21 } 22 23 $test = '"earth",1,"a1, a2","moon",9.374'; 24 print "string = \'$test\'\n"; 25 print "Using parse_csv\n:"; 26 @fields = parse_csv($test); 27 foreach $i (@fields) { 28 print "$i\n"; 29 } 30 31 print "Using Text::ParseWords\n:"; 32 # @words = "ewords($delim, $keep, @lines); 33 #The $keep argument is a boolean flag. If true, then the 34 #tokens are split on the specified delimiter, but all other 35 #characters (quotes, backslashes, etc.) are kept in the 36 #tokens. If $keep is false then the &*quotewords() 37 #functions remove all quotes and backslashes that are not 38 #themselves backslash-escaped or inside of single quotes 39 #(i.e., "ewords() tries to interpret these characters 40 #just like the Bourne shell). 41 42 @fields = quotewords(',',0,$test); 43 foreach $i (@fields) { 44 print "$i\n"; 45 }
Las subrutinas en Perl reciben sus argumentos en el array
@_
. Si la lista de argumentos contiene listas, estas
son ``aplanadas'' en una única lista. Si, como es el caso, la subrutina
ha sido declarada antes de la llamada, los argumentos pueden
escribirse sin paréntesis que les rodeen:
@fields = parse_csv $test;
Otro modo de llamar una subrutina es usando el prefijo &
,
pero sin proporcionar lista de argumentos.
@fields = &parse_csv;En este caso se le pasa a la rutina el valor actual del array
@_
.
Los operadores push
(usado en la línea 17) y pop
trabajan
sobre el final del array. De manera análoga los operadores
shift
y unshift
lo hacen sobre el comienzo.
El operador ternario ?
trabaja de manera análoga como lo hace en C.
El código del push
podría sustituirse por este otro:
push(@fields, $+);Puesto que la variable
$+
contiene la cadena que ha casado
con el último paréntesis que haya casado en el ultimo ``matching''.
La segunda parte del código muestra que existe un
módulo en Perl, el módulo Text::Parsewords
que proporciona la rutina quotewords
que hace la misma función que nuestra subrutina.
Sigue un ejemplo de ejecución:
> csv.pl string = '"earth",1,"a1, a2","moon",9.374' Using parse_csv :earth 1 a1, a2 moon 9.374 Using Text::ParseWords :earth 1 a1, a2 moon 9.374
El siguiente programa evalúa si un número es primo o no:
pl@nereida:~/Lperltesting$ cat -n isprime.pl 1 #!/usr/bin/perl -w 2 use strict; 3 4 my $num = shift; 5 die "Usage: $0 integer\n" unless (defined($num) && $num =~ /^\d+$/); 6 7 if (("1" x $num) =~ /^(11+)\1+$/) { 8 my $factor = length($1); 9 print "$num is $factor x ".$num/$factor."\n"; 10 } 11 else { 12 print "$num is prime\n"; 13 }Siguen varias ejecuciones:
pl@nereida:~/Lperltesting$ ./isprime.pl 35.32 Usage: ./isprime.pl integer pl@nereida:~/Lperltesting$ ./isprime.pl 47 47 is prime pl@nereida:~/Lperltesting$ ./isprime.pl 137 137 is prime pl@nereida:~/Lperltesting$ ./isprime.pl 147 147 is 49 x 3 pl@nereida:~/Lperltesting$ ./isprime.pl 137 137 is prime pl@nereida:~/Lperltesting$ ./isprime.pl 49 49 is 7 x 7 pl@nereida:~/Lperltesting$ ./isprime.pl 47 47 is prime
Según dice la entrada Diophantine_equation en la wikipedia:
In mathematics, a Diophantine equation is an indeterminate polynomial equation that allows the variables to be integers only.
La siguiente sesión con el depurador muestra como se puede resolver una ecuación lineal diofántica con coeficientes positivos usando una expresión regular:
DB<1> # Resolvamos 3x + 2y + 5z = 40 DB<2> x ('a'x40) =~ /^((?:...)+)((?:..)+)((?:.....)+)$/ 0 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa' 1 'aa' 2 'aaaaa' DB<3> x map { length } ('a'x40) =~ /^((?:...)+)((?:..)+)((?:.....)+)$/ 0 33 1 2 2 5 DB<4> @c = (3, 2, 5) DB<5> x map { length($_) / $c[$i++] } ('a'x40) =~ /^((?:...)+)((?:..)+)((?:.....)+)$/ 0 11 1 1 2 1 DB<6> p 3*11+2*1+5*1 40
Usando el verbo (*FAIL)
es posible obtener todas las soluciones:
main::(-e:1): 0 DB<1> sub equ { my @c = @_; print "\t3*$c[0]+2*$c[1]+5*$c[2] = ",3*$c[0]+2*$c[1]+5*$c[2],"\n" } DB<2> sub f { my @c = ((length($1)/3), (length($2)/2), (length($3)/5)); equ(@c); } DB<3> x ('a'x40) =~ /^((?:...)+)((?:..)+)((?:.....)+)$(?{ f() })(*FAIL)/x 3*11+2*1+5*1 = 40 3*9+2*4+5*1 = 40 3*8+2*3+5*2 = 40 3*7+2*7+5*1 = 40 3*7+2*2+5*3 = 40 3*6+2*6+5*2 = 40 3*6+2*1+5*4 = 40 3*5+2*10+5*1 = 40 3*5+2*5+5*3 = 40 3*4+2*9+5*2 = 40 3*4+2*4+5*4 = 40 3*3+2*13+5*1 = 40 3*3+2*8+5*3 = 40 3*3+2*3+5*5 = 40 3*2+2*12+5*2 = 40 3*2+2*7+5*4 = 40 3*2+2*2+5*6 = 40 3*1+2*16+5*1 = 40 3*1+2*11+5*3 = 40 3*1+2*6+5*5 = 40 3*1+2*1+5*7 = 40 empty array DB<4>
El siguiente programa recibe en línea de comandos los coeficientes y término inependeinte de una ecuación lineal diofántica con coeficientes positivos y muestra todas las soluciones. El algoritmo primero crea una cadena conteniendo el código Perl que contiene la expresión regular adecuada para pasar luego a evaluarlo:
pl@nereida:~/Lperltesting$ cat -n diophantinesolvergen.pl 1 #!/usr/local/lib/perl/5.10.1/bin//perl5.10.1 -w 2 use v5.10; 3 use strict; 4 5 # Writes a Perl solver for 6 # a1 x1 + a2 x2 + ... + an xn = b 7 # a_i and b integers > 0 8 # 9 10 my $b = pop; 11 my @a = @ARGV; 12 my $debug = 1; 13 14 my $b1 = '1'x$b; 15 my @a1 = map { '1'x$_ } @a; 16 my @index = map { 'length($'.$_.")/".$a[$_-1] } 1..(@a); 17 my $aux = join ",", @index; 18 19 my $regexp = '^'; 20 $regexp .= "((?:$_)+)" for @a1; 21 22 $regexp .= '$(?{ f() })(*FAIL)'; 23 24 my $solver = <<"SOLVER"; 25 my \@stack; 26 sub f { 27 my \@s = ($aux); 28 push \@stack, [ \@s ]; 29 } 30 31 q{$b1} =~ m{$regexp}x; 32 33 return \@stack; 34 SOLVER 35 36 print "Solver:\n--------\n$solver\n--------\n" if $debug; 37 38 my @stack = eval $solver; 39 40 say("@$_") for @stackSigue un ejemplo de ejecución:
pl@nereida:~/Lperltesting$ ./diophantinesolvergen.pl 3 2 5 40 Solver: -------- my @stack; sub f { my @s = (length($1)/3,length($2)/2,length($3)/5); push @stack, [ @s ]; } q{1111111111111111111111111111111111111111} =~ m{^((?:111)+)((?:11)+)((?:11111)+)$(?{ f() })(*FAIL)}x; return @stack; -------- 11 1 1 9 4 1 8 3 2 7 7 1 7 2 3 6 6 2 6 1 4 5 10 1 5 5 3 4 9 2 4 4 4 3 13 1 3 8 3 3 3 5 2 12 2 2 7 4 2 2 6 1 16 1 1 11 3 1 6 5 1 1 7
En la páginas de Retos Matemáticos de
puede encontrarse el siguiente problema:
¿Qué edad tendrán las tres hijas?
¿Podemos ayudarnos de una expresión regular para resolver el problema? Al ejecutar el siguiente programa:
pl@nereida:~/Lperltesting$ cat -n playspiano.pl 1 #!/usr/local/lib/perl/5.10.1/bin//perl5.10.1 -w 2 use v5.10; 3 use strict; 4 use List::Util qw{sum}; 5 6 local our %u; 7 sub f { 8 my @a = @_; 9 @a = sort { $b <=> $a } (length($a[1]), length($a[0])/length($a[1]), 36/length($a[0]) ); 10 11 local $" = ", "; 12 say "(@a)\t ".sum(@a) unless exists($u{"@a"}); 13 $u{"@a"} = undef; 14 } 15 16 say "SOL\t\tNUMBER"; 17 my @a = ('1'x36) =~ 18 /^((1+)\2+)(\1+)$ 19 (?{ f($1, $2, $3) 20 }) 21 (*FAIL) 22 /x;
obtenemos la salida:
pl@nereida:~/Lperltesting$ ./playspiano.pl SOL NUMBER (9, 2, 2) 13 (6, 3, 2) 11 (4, 3, 3) 10 (18, 2, 1) 21 (12, 3, 1) 16 (9, 4, 1) 14 (6, 6, 1) 13
Explique el funcionamiento del programa. A la vista de la salida ¿Cuáles eran las edades de las hijas?
Para una definición del problema vea la sección El Problema de la Mochila 0-1 en los apuntes de LHP
¡Si lo logra merece el premio a la solución mas freak que se haya encontrado para dicho problema!
Véase también:
/g
. En cualquier otro caso retorna el valor falso.
1 #!/usr/bin/perl -w 2 undef($/); 3 $paragraph = <STDIN>; 4 $count = 0; 5 $count = ($paragraph =~ s/Mister\b/Mr./ig); 6 print "$paragraph"; 7 print "\n$count\n";El resultado de la ejecución es el siguiente:
> numsust.pl Dear Mister Bean, Is a pleasure for me and Mister Pluto to invite you to the Opening Session Official dinner that will be chaired by Mister Goofy. Yours sincerely Mister Mickey Mouse Dear Mr. Bean, Is a pleasure for me and Mr. Pluto to invite you to the Opening Session Official dinner that will be chaired by Mr. Goofy. Yours sincerely Mr. Mickey Mouse 4
pl@nereida:~/Lperltesting$ cat -n expandtabs.pl 1 #!/usr/bin/perl -w 2 use strict; 3 4 my @string = <>; 5 6 for (@string) { 7 while (s/\t+/' ' x (length($&)*8 - length($`)%8)/e) {} 8 print $_; 9 }Sigue un ejemplo de ejecución:
pl@nereida:~/Lperltesting$ cat -nt tabs.in 1 012345670123456701234567012345670 2 one^Itwo^I^Ithree 3 four^I^I^I^Ifive 4 ^I^Itwo pl@nereida:~/Lperltesting$ ./expandtabs.pl tabs.in | cat -tn 1 012345670123456701234567012345670 2 one two three 3 four five 4 two
while
por una opción /g
?
pl@nereida:~/Lperltesting$ cat -n ./expandtabs2.pl 1 #!/usr/bin/perl -w 2 use strict; 3 4 my @string = <>; 5 6 for (@string) { 7 s/\t+/' ' x (length($&)*8 - length($`)%8)/ge; 8 print $_; 9 }¿Porqué?
perl -e 's/nereida\.deioc\.ull\.es/miranda.deioc.ull.es/gi' -p -i.bak *.html
Este programa sustituye la palabra original (g)lobalmente e i)gnorando el ``case'') en todos los ficheros *.html
y para cada uno de ellos crea una copia de seguridad *.html.bak
.
Otro ejemplo: la sustitución que sigue ocurre en todos los ficheros info.txt
en todos
los subdirectorios de los subdirectorios que comiencen por alu
:
perl -e 's/\|hyperpage//gi' -p -i.bak alu*/*/info.txt
Las opciones de línea de comandos significan lo siguiente:
while (<>) { ... # your script goes here } continue { print; }
file.in
a file.in.ext
, abriendo el de salida con el mismo nombre del fichero de entrada file.in
.
Se selecciona dicho fichero como de salida por defecto para las sentencias print
. Si se proporciona una extensión se hace una copia de seguridad. Si no, no se hace copia de seguridad.
En general las opciones pueden ponerse en la primera línea del ``script'', donde se indica el intérprete. Asi pues, decir
perl -p -i.bak -e "s/foo/bar/;"
es equivalente a usar el ``script'':
#!/usr/bin/perl -pi.bak s/foo/bar/;
Casiano Rodríguez León