Utilice el operador grep para seleccionar una sublista de una lista dada.
El operador tiene dos formas de uso.
El siguiente ejemplo, muestra la primera forma de uso:
crea la lista @withnumbers
a partir de una lista
@lines
formada por aquellos elementos que contienen uno o más dígitos.
@withnumbers = grep /\d+/, @lines;
grep
recibe como primer argumento una expresión regular y como segundo una lista.
En Perl las expresiones regulares se escriben entre barras de división. La notación
\d
casa con cualquier dígito decimal. Así pues el cierre positivo casará con cualquier
número entero sin signo.
Para saber más sobre expresiones regulares, estudie el capítulo
3.
El siguiente código corresponde a la segunda forma de uso:
guarda en $n_undef
el número de items undef
en el array v
$n_undef = grep { !defined($_)} @v;
Obsérvese que en esta segunda forma de uso no hay coma de separación entre el bloque
y el array.
En general, el primer argumento de grep
es un bloque en el cual se utiliza $_
como referente a cada elemento de la lista, y se devuelve un valor lógico. Los restantes argumentos constituyen la lista
de items sobre la que hay que buscar. El operador grep
evalúa la expresión una vez para cada
item en la lista, como si se tratara de un bucle foreach
.
Cuando el bloque esta constituido por una única expresión se puede escribir simplemente la expresión,
separándola mediante una coma de la lista.
@b = grep {not $_ % 5} @s;
Si lo que se quiere es construir un array transformado del array inicial, se debe usar map:
@sizes = map {-s $_ } @files;El operador -s retorna el tamaño de un fichero. Este código genera un array conteniendo los tamaños de los ficheros especificados en
@files
. La forma de uso anterior utiliza la sintáxis
map bloque array
. Observe la ausencia de coma.
@a = grep { defined } map { /(\d+)/; $1 } glob('/tmp/*')El operador glob produce una lista con los ficheros descritos por la expresión shell que se le pasa como argumento (vea
perldoc -f glob
). La expresión regular /(\d+)/
casa
con números enteros sin signo. Al estar paréntizada la cadena que ha casado
queda automáticamente guardada en la variable especial $1
.
El valor devuelto por un bloque es la última sentencia evaluada, en
este caso es el valor de $1
.
Cuando un casamiento con una expresión regular tiene lugar en un contexto de lista se devuelve una lista con las cadenas que han casado con los paréntesis:
DB<0> $a = "hola 123 b 56 c" DB<1> @a = $a =~ m{(\d+)\s(\w+)} DB<2> x @a 0 123 1 'b' DB<3> @a = $a =~ m{(\d+)\s(\w+)}g DB<4> x @a 0 123 1 'b' 2 56 3 'c'Quizá le ayude a entender la salida la siguiente sesión con el depurador:
DB<0> x glob('/tmp/*.pl') 0 '/tmp/cgisearch.pl' 1 '/tmp/ficha.pl' 2 '/tmp/uploadpractica.config.pl' 3 '/tmp/uploadpractica.pl' DB<1> x map { m{/([^u/]+)$}; $1 } glob('/tmp/*.pl') 0 'cgisearch.pl' 1 'ficha.pl' 2 undef 3 undef DB<2> x grep { defined} map { m{/([^u/]+)$}; $1 } glob('/tmp/*.pl') 0 'cgisearch.pl' 1 'ficha.pl'
@a = map { /(\d+)/ } glob('/tmp/*')produce el mismo resultado que la anterior. ¿Porqué? ¿Que retorna cada una de las evaluaciones individuales del bloque? ¿En que contexto lista o escalar se evalúa
/(\d+)/
?
Observe este comando en el depurador:
DB<1> x @a = map { /(\d+)/ } ("a123", "b", "c42") 0 123 1 42
@a = map { scalar(/(\d+)/) } glob('/tmp/*')
Perl proporciona las funciones push, pop, shift y unshift
que permiten trabajar el array
o lista como si de una pila o cola se tratase.
La función push
tiene la sintáxis:
push(ARRAY,LIST)
empuja el valor de LIST
en el ARRAY
. La longitud del ARRAY
se incrementa en la longitud de
LIST
. Es lo mismo que hacer:
for $value (LIST) { $ARRAY[++$#ARRAY] = $value; }
La función pop
tiene la sintáxis:
pop(ARRAY) pop ARRAY
pop ARRAY
tiene el mismo efecto que:
$tmp = $ARRAY[$#ARRAY]; $#ARRAY--
Si no hay elementos en ARRAY
, devuelve el valor undef
.
Las funciones shift
y unshift
actuán de manera similar a
push
y pop
pero utilizando el comienzo de la lista en vez del final
de la misma.
Las funciones push
, pop
, shift
y unshift
son un caso particular de la función splice, la cual cambia
los elementos de un ARRAY
. La función splice
toma 4 argumentos:
el ARRAY
a modificar, el índice OFFSET
en el cual es modificado,
el número de elementos a suprimir LENGTH
y la lista de elementos extra a insertar.
splice(ARRAY,OFFSET,LENGTH,LIST) splice(ARRAY,OFFSET,LENGTH) splice(ARRAY,OFFSET)
La función splice
devuelve los elementos suprimidos del ARRAY
.
Si se omite LENGTH
se suprime todo desde OFFSET
hacia adelante. Se cumplen las siguientes equivalencias
push(@a,$x) splice(@a,$#a+1,0,$x) pop(@a) splice(@a,-1) shift(@a) splice(@a,0,1) unshift(@a,$x) splice(@a,0,0,$x) $a[$x] = $y splice(@a,$x,1,$y);
No se puede acortar un array asignando undef a sus elementos del final.
Para acortarlo se debe asignar $#a
o utilizar un operador como pop
ó splice.
@a = 1 .. 10; $a[9] = undef; # @a = (1 ..9, undef) $x = pop @a; # @a = (1 ..9) splice @a, -2; # @a = (1 ..7) OFFSET = -2. Como se ha suprimido # LENGTH, se suprime desde el penúltimo elemento # hacia adelante $#a = 4; # @a = (1 ..5)
La función join convierte un arreglo en un escalar
La llamada a join EXPR, LIST
concatena las cadenas en LIST
separadas por EXPR
:
@a = ('a'..'e'); $a = join ":", @a # $a queda con "a:b:c:d:e";
La función split es la inversa de join:
convierte un escalar en un arreglo.
split /PATTERN/,EXPR,LIMIT
retorna el arreglo resultante
de partir la cadena EXPR
. Si se especifica LIMIT
indica el número máximo de campos en los que se divide.
lhp@nereida:~/projects/perl/src$ perl -wde 0 main::(-e:1): 0 DB<1> $a = 'a:b:c:d:e' DB<2> @a = split /:/, $a DB<3> x @a 0 'a' 1 'b' 2 'c' 3 'd' 4 'e' DB<4> @a = split /:/, $a, 3 DB<5> x @a 0 'a' 1 'b' 2 'c:d:e' DB<6> $a = 'a:b:c:d::' DB<7> @a = split /:/, $a DB<8> x @a # Los nulos finales se suprimen 0 'a' 1 'b' 2 'c' 3 'd' DB<9> @a = split /:/, $a, -1 DB<10> x @a # No si LIMIT es negativo 0 'a' 1 'b' 2 'c' 3 'd' 4 '' 5 ''Observe que en
split
el primer parámetro es una expresión regular
mientras que en join
es un carácter.
El operador sort toma una lista de valores y los ordena según el alfabeto ASCII. Por ejemplo:
@rocks = qw/ bedrocks slate rubble granite /; @sorted = sort (@rocks); # bedrocks granite rubble slateEl operador sort de Perl utiliza los operadores de cadenas por defecto:
:~/perl/src> perl -de 0 main::(-e:1): 0 DB<1> print "a" == "b" 1 DB<2> @x = sort (10, 2); DB<3> p "@x" 10 2
La función sort
admite como primer argumento un bloque que determina la función de comparación.
Dicho bloque depende de dos variables ''especiales'' a y b.
El valor retornado por un bloque es el valor asociado con la última sentencia del bloque.
En este caso sort
espera que el bloque
devuelva -1, 0 ó 1. Dicho valor es utilizado como elemento de comparación.
Por ejemplo:
lhp@nereida:~/Lperl/src$ perl -dwe 0 main::(-e:1): 0 DB<1> @a = (4, 7, 9, 12, -1) DB<2> p 4 <=> 7 -1 DB<3> p 9 <=> 2 1 DB<4> p 13 <=> 12+1 0 DB<5> @a = sort { $a <=> $b } @a; DB<6> p "@a" -1 4 7 9 12 DB<7> @a = sort { $b <=> $a } @a; DB<8> p "@a" 12 9 7 4 -1
El siguiente ejemplo ordena los usuarios de un sistema unix atendiendo a su
uid
:
lhp@nereida:~/projects/perl/src$ cat -n sortperuid.pl 1 #!/usr/bin/perl -w 2 use strict; 3 my @user = grep { $_ !~ /^#/ } `cat /etc/passwd`; 4 my (@name, @uid, $x); 5 6 for (my $i=0; $i < @user; $i++) { 7 ($name[$i], $x, $uid[$i]) = split ':', $user[$i]; 8 } 9 10 @name = @name[ 11 sort {$uid[$a] <=> $uid[$b]} 0..$#name 12 ]; 13 14 local $" = "\n"; 15 print "@name\n";En la línea 3 se obtiene en
@user
la lísta de líneas
no comentadas en /etc/passwd
.
en las líneas 6-8 se inicializan los arrays @name
y @uid
a los nombres (login) y uid de los usuarios
(campos primero y tercero de /etc/passwd
).
Las líneas 10-12 ordenan primero la lista 0..$#name
de acuerdo con el
valor de uid
. El nuevo conjunto ordenado de índices es utilizado para
reindexar el array name
.
El operador sort
en un contexto escalar devuelve undef
.
El módulo List::Util provee un conjunto de funciones para el manejo de listas. Veamos algunos ejemplos de uso de List::Util:
DB<1> use List::Util qw(first max maxstr min minstr reduce shuffle sum) DB<2> @a = map { int(rand 20) } 1..5 DB<3> x @a 0 8 1 9 2 4 3 16 4 5 DB<4> x min @a 0 4 DB<5> x max @a 0 16 DB<6>x first { $_ > 8 } @a 0 9 DB<7> x sum @a 0 42 DB<8> x shuffle @a 0 8 1 5 2 16 3 4 4 9 DB<9> x reduce { $a." $b" } @a 0 '8 9 4 16 5'
Existe también un módulo List::MoreUtils que provee mas funciones para el manejo de listas. He aqui un fragmento de la sección SYNOPSIS de la documentación:
use List::MoreUtils qw(any all none notall true false firstidx first_index lastidx last_index insert_after insert_after_string apply after after_incl before before_incl indexes firstval first_value lastval last_value each_array each_arrayref pairwise natatime mesh zip uniq minmax);
Veamos algunos ejemplos de uso de List::MoreUtils:
lhp@nereida:~/Lperl/src/XSUB/h2xsexample/Coord/script$ perl -de 0 main::(-e:1): 0 DB<1> use List::MoreUtils qw(:all) # Importar todas las funciones DB<2> @a = (1..8,-2,-3) DB<4> print "@a" if any { $_ > 0 } @a 1 2 3 4 5 6 7 8 -2 -3 DB<5> print "@a" if all { $_ > 0 } @a DB<6> print (false {$_ > 0} @a),"\n" # Número de elementos para los que es falsa la condición 2 DB<7> print (firstidx {$_ < 0} @a),"\n" 8 DB<8> @a = (1..3,2..5,3..6) DB<9> x uniq(@a) # Array con los elementos distintos 0 1 1 2 2 3 3 4 4 5 5 6 DB<8> @a = 1..5; @b = 'a'..'e'; @c = 10..14 DB<9> x mesh @a, @b, @c # Mezcla los 3 arrays 0 1 1 'a' 2 10 3 2 4 'b' 5 11 6 3 7 'c' 8 12 9 4 10 'd' 11 13 12 5 13 'e' 14 14
El array especial @ARGV contiene la lista de argumentos del programa.
El array @ARGV
es usado como array por defecto cuando se realiza una
operación de arrays dentro del
programa principal:
nereida:~/etc> perl -wde 0 one two three main::(-e:1): 0 DB<1> p "@ARGV" one two three DB<2> p $^X # La variable mágica $^X tiene el camino al intérprete Perl utilizado /usr/bin/perl DB<3> $a = shift # No se especifica el arrray: es @ARGV DB<4> x $a 0 'one' DB<5> $b = pop DB<6> x $b 0 'three' DB<7> x $^O # La variable $^O contiene el nombre del sistema operativo. 0 'linux' DB<8> x $0 # La variable mágica $0 contiene el nombre del programa 0 '-e'
-e command
su nombre es -e
.
$^O
contiene el nombre del sistema operativo.
En contra de lo que quizá algunos podrían esperar, una lista que contiene a otra lista:
@virtues = ("faith", "hope", ("love", "charity"));
no produce una jerarquía de listas, sino que la lista es aplanada y es lo mismo que si nunca se hubieran puesto los paréntesis. Esto es, es equivalente a:
@virtues = ("faith", "hope", "love", "charity");