Patricio Poblete (ppoblete@dcc.uchile.cl) |
Perl es un lenguaje interpretado, que fue creado e implementado por Larry Wall. Perl combina características de un shell, de sed, de awk, de C, e incluso algo de BASIC.
Es un lenguaje muy útil para desarrollar aplicaciones en forma rápida, con manejo de archivos y acceso a las funciones del sistema. También ha resultado ser uno de los lenguajes preferidos para implementar aplicaciones en el web.
hola |
#!/usr/bin/perl print "¡Hola!\n";
saluda |
#!/usr/bin/perl # Lee un nombre e imprime saludo print "Por favor, escribe tu nombre: "; $nombre = <STDIN>; chop($nombre); print "¡Hola $nombre!\n";
Notas:
#
y llega hasta el fin de la linea.
$nombre
es una variable escalar, que en este caso
se utiliza para almacenar un string.
<STDIN>
lee una línea desde la entrada estándar.
La línea incluye el \n
final.
chop($nombre)
le quita el último carácter al string.
chomp($nombre)
le quita el último carácter al string
sólo si éste es un newline.
"...$nombre..."
expande la variable dentro del
string ('...$nombre...'
no expande).
$
.
Ejemplos:
$max
, $linea
, $x1
,
$x_1
$1
, $2
, ..., $$
... (variables
usadas por Perl)
$_
(variable default para muchas operaciones)
cat |
#!/usr/bin/perl while(<STDIN>) # Lee y deja en $_ { print; # Imprime $_ }
42
3.14159
0.2e-5
0xff
(hex)
0377
(octal)
1995
1_995
(igual al anterior)
Strings:
"..."
$
var,
y también se interpretan \n
, etc.
'...'
\'
y \\
here |
#!/usr/bin/perl print <<'END-OF-TEXT'; Este es un texto que se toma textualmente hasta que aparece la marca definida al comienzo. END-OF-TEXT
<<'...'
<<"..."
if( EXPR ) BLOCK
if( EXPR ) BLOCK else BLOCK
if( EXPR ) BLOCK elsif( EXPR ) BLOCK ... else
BLOCK
donde un BLOCK es de la forma
{ EXPR; ... ; EXPR; }
(el último ;
es opcional).
Una forma alternativa es
EXPR if EXPR
EXPR unless EXPR
Ejemplo:
$max = $x unless $max<$x;
LABEL while( EXPR ) BLOCK
LABEL while( EXPR ) BLOCK continue BLOCK
LABEL for( EXPR; EXPR; EXPR ) BLOCK
LABEL foreach VAR ( ARRAY ) BLOCK
LABEL BLOCK continue BLOCK
El LABEL es opcional, y permite identificar el ciclo para ser
usado por las instrucciones siguientes:
last LABEL # Termina el ciclo
next LABEL # Va a la siguiente iteración
redo LABEL # Ejecuta de nuevo el cuerpo de la iteración
Si el LABEL se omite, la instrucción se aplica al ciclo más
cercano.
""
, "0"
ó 0
,
se interpreta como FALSO; en caso contrario, VERDADERO.
Si una variable es indefinida, al ser evaluada da el string vacío, el cual en un contexto booleano da FALSO. Es frecuente aplicar esto de la manera siguiente:
Ejemplo:
$lim=100 unless $lim; # indefinido => 100
aunque en rigor esto debería escribirse:
$lim=100 unless defined($lim); # indefinido => 100
También existen conectivos lógicos: !
,
&&
y ||
.
Estos operadores tienen alta prioridad.
Existen también los sinónimos de baja prioridad not
,
and
y or
.
<, >, <=, >=, ==, !=
También existe el operador <=>
, que retorna
-1, 0 ó +1.
Para comparar escalares como strings, se usan los operadores:
lt, gt, le, ge, eq, ne
En análogo para strings de <=>
es cmp
.
headers |
#!/usr/bin/perl while(<>) { print if /^From:|^Subject:/; last if /^$/; # Para con una línea en blanco }Notas:
<>
lee desde los archivos
entregados en la línea de comando, o desde STDIN
si no hay argumentos.
La linea leída queda en $_
, retorna el valor
indefinido al llegar al fin de archivo.
/.../
hace match sobre $_
$var =~ /.../
$var =~ m:...:
@
,
y sus elementos se subindican de cero en adelante.
paco |
#!/usr/bin/perl @p = ("hugo", "paco", "luis"); print $p[1], "\n"; # imprime "paco"Notas:
@p
,
un elemento particular se denota $p[k]
, porque es
escalar.
@a
se denota $#a
.
echo |
#!/usr/bin/perl for( $i=0; $i<=$#ARGV; ++$i ) { print $ARGV[$i], ($i<$#ARGV ? " " : "\n"); }Notas:
@ARGV
trae los argumentos de la línea de comando.
No incluye el nombre con el cual fue ejecutado el programa.
( ... ? ... : ... )
es una expresión condicional.
echo2 |
#!/usr/bin/perl print join(" ", @ARGV), ($#ARGV>=0 ? "\n" : "" );Nota:
join
concatena todos los elementos del arreglo,
separados por el string indicado como primer parámetro.
join
es split
.
El siguiente programa calcula el número total de segundos
correspondientes a una hora de la forma "hh:mm"ss":
hora1 |
#!/usr/bin/perl print "Ingrese una hora en formato hh:mm:ss\n"; chop($hms=<>); # Combina lectura y chop ($h, $m, $s) = split(/:/, $hms); print "Esto equivale a ", $h*3600+$m*60+$s, " segundos\n";Notas:
hora2 |
#!/usr/bin/perl print "Ingrese una hora en formato hh:mm:ss\n"; chop($hms=<>); # Combina lectura y chop $hms =~ /(..):(..):(..)/; print "Esto equivale a ", $1*3600+$2*60+$3, " segundos\n";Notas:
$1, $2, $3
representan a los tres substrings correspondientes
a las partes parentizadas del pattern.
push(@a,$x)
- Agrega
$x
al final de@a
$x = pop(@a)
- Extrae el último elemento de
@a
unshift(@a,$x)
- Agrega
$x
al comienzo de@a
$x = shift(@a)
- Extrae el primer elemento de
@a
%
, y el subíndice
se encierra entre { }
.
Ejemplo: Resumir records de entrada de la forma "userid value"
.
resume |
#!/usr/bin/perl while(<>) { chop; # opera sobre $_ ($u, $v) = split; $total{$u} += $v; } foreach $u (sort keys %total) { printf "%-8s %4d\n", $u, $total{$u} }
Notas:
chop
y split
operan sobre $_
al omitirse sus parámetros.
keys
entrega un arreglo con todas
las llaves del hash.
El operador sort
ordena un arreglo.
foreach $u
hace que $u
vaya tomando todos
los valores del arreglo que se indica.
printf
permite imprimir con formato, al estilo de C.
sprintf
FORMAT, LIST,
que entrega un string formateado.
dim |
#!/usr/bin/perl %dim = ( "José", "Pepe", "Patricio", "Pato", "Francisco", "Pancho" ); while(<>) { chop; $_ = $dim{$_} if $dim{$_}; print "¡Hola $_!\n"; }Notas:
=>
. Por ejemplo,
%dim = ( "José" => "Pepe", "Patricio" => "Pato", ...
Ejemplo:
pwd |
#!/usr/bin/perl print "El directorio actual es $ENV{'PWD'}\n";
show
archivo
show |
#!/usr/bin/perl die "Use: show archivo\n" unless $#ARGV==0; open(IN,$ARGV[0]) || die "No se puede abrir el archivo\n"; while(<IN>) { print; # imprime $_ } close(IN);Notas:
die "..."
imprime el mensaje y termina el programa.
open(FILE,"...")
abre el archivo; retorna indefinido si falla.
close(FILE)
cierra el archivo (automático al terminar el
programa).
Ejemplo: copy
from-file to-file
copy |
#!/usr/bin/perl $#ARGV==1 || die "Use: copy from-file to-file\n"; open(IN,"<$ARGV[0]") || die "No se puede abrir archivo de entrada\n"; open(OUT,">$ARGV[1]") || die "No se puede abrir archivo de salida\n"; while(<IN>) { print OUT; }Notas:
print
como printf
tienen un
FILE opcional como primer argumento. Si se omite, se supone
STDOUT
.
open
open
es
open FILE, filename
Los argumentos también pueden ir entre paréntesis.
El filename puede ser de las formas siguientes:
"..." | read |
"<..." | |
">..." | write |
">>..." | append |
"+>..." | read/write |
"|..." | pipe to command |
"...|" | pipe from command |
Ejemplo:
$linea = <STDIN>; # lee una línea
@lineas = <STDIN>; # lee TODAS las líneas
El siguiente programa es una versión de cat
de Unix
que usa mucha memoria:
cat2 |
#!/usr/bin/perl @lineas = <>; # leer todas las líneas print @lineas; # e imprimirlas
Más aún, como print
espera una lista de parámetros,
el contexto a la derecha de print
es de arreglo.
Así, el siguiente programa es equivalente al anterior:
cat3 |
#!/usr/bin/perl print <>;
De manera similar, muchos operadores se extienden a arreglos. Por ejemplo,
chop(@lineas);
quita los \n
a cada uno de los elementos del arreglo.
grep
y map
grep(EXPR, ARRAY)
aplica
la EXPR a cada uno de los elementos del ARRAY, y
retorna un arreglo conteniendo a los elementos que arrojaron
valor verdadero.
La operación map
(de Perl5) es similar, pero retorna todos los
elementos.
Ejemplo:
titulos |
#!/usr/bin/perl @a = <>; print grep(/h3/,@a); # Imprimir todos los títulos h3 print "-" x 60, "\n"; # Imprime "-" 60 veces print grep(s/h3/h2/g,@a); # Modificar e imprimir líneas con h3 print "-" x 60, "\n"; print map(s/h3/h2/g,@a); # Modificar h3; imprimir todas las líneas
*, +, (, ),
, etc., los cuales se
escapan con \
.
. | Cualquier carácter excepto newline |
(...) | Agrupa y recuerda en $1, $2 ,...
el substring detectado |
^ | Comienzo de línea |
$ | Fin de línea |
[...] | Clase de caracteres |
[^...] | Clase de caracteres negada |
(...|...|...) | Alternativas |
* | Repite lo anterior 0 o más veces |
+ | Repite lo anterior 1 o más veces |
{m,n} | Repite mínimo m ,
máximo n veces |
\w | Carácter alfanumérico | \W | Carácter no alfanumérico | |
\s | Espacio (blanco, tab) | \S | No espacio | |
\d | Carácter numérico | \D | Carácter no numérico | |
\b | Borde de palabra | \B | No borde de palabra |
Ejemplo:
hora |
#!/usr/bin/perl open(DATE, "date|"); $date = <DATE>; # Tue Aug 15 16:17:12 CST 1995 ($h, $m) = ( $date =~ /\w+ \w+ \d+ (\d+):(\d+):\d+/ ); print "Son las ", ($h<=12? $h : $h-12), ($m==0? "" : " con $m minutos"), "\n"; # Son las 4 con 17 minutos
sub subname { statement1; statement2; . . . }
Ejemplo:
saluda2 |
#!/usr/bin/perl sub saluda { print "¡Hola!\n"; } # A continuación, la llamada: saluda;
@_
.
Ejemplo:
saluda3 |
#!/usr/bin/perl sub saluda { print "¡Hola $_[0]!\n"; } saluda("Juan"); saluda("María");
Ejemplo:
suma |
#!/usr/bin/perl sub suma { $_[0]+$_[1]; } print suma(2,3), "\n";
my
es local a la
subrutina.
Es frecuente copiar los argumentos a variables locales. Con esto se consiguen dos efectos: darles nombres más significativos, y hacer que el traspaso sea por valor (el default es por referencia).
Ejemplo:
suma2 |
#!/usr/bin/perl sub suma { my($a,$b) = @_; $a+$b; } print suma(2,3), "\n";
suman |
#!/usr/bin/perl sub suma { my $s = 0; # en realidad, no hace falta # inicializar my $x; foreach $x( @_ ) { $s += $x; } $s; } print suma(12,5,21,14), "\n"; print suma(1..5), "\n"; # 1+2+...+5
potencia |
#!/usr/bin/perl sub potencia { my($a,$n) = @_; if( $n == 0 ) { 1; } elsif( $n%2 == 1 ) { $a*potencia($a,$n-1); } else { potencia($a*$a,$n/2); } } print potencia(2,13), "\n";
rand
EXPR
time
length
EXPR
-r
FILE
rename
OLDNAME, NEWNAME
sleep
EXPR
pack
y unpack
para
manipular records binarios.
En Perl, una clase se implementa mediante un package.
Empleado1.pm |
package Empleado1; sub new { my($pkg, $nombre, $rut, $sueldo) = @_; my $e = { nombre => $nombre, rut => $rut, sueldo => $sueldo }; return bless $e, $pkg; } sub impr { my $e=shift; print "Nombre: ", $e->{rut}, "\n", "RUT: ", $e->{nombre}, "\n", "Sueldo: ", $e->{sueldo}, "\n"; } sub aumenta_sueldo { my($e, $delta) = @_; $e->{sueldo} += $delta; } sub sueldo # método de acceso { my $e=shift; return $e->{sueldo}; } 1;
Ejemplo:
empleado1 |
#!/usr/bin/perl use Empleado1; $e1=new Empleado1("Juan Pérez", "3.141.159-2", 350000); $e1->impr; $e1->aumenta_sueldo(50000); print "\n"; $e1->impr;
Existen también métodos de la clase. Por ejemplo, supongamos que se desea llevar un contador global de cuantos empleados existen:
Empleado2.pm |
package Empleado2; my $nemp=0; sub new { my($pkg, $nombre, $rut, $sueldo) = @_; my $e = { nombre => $nombre, rut => $rut, sueldo => $sueldo }; ++$nemp; return bless $e, $pkg; } sub nemp # metodo de la clase { return $nemp; } sub impr { my $e=shift; print "Nombre: ", $e->{rut}, "\n", "RUT: ", $e->{nombre}, "\n", "Sueldo: ", $e->{sueldo}, "\n"; } sub aumenta_sueldo { my($e, $delta) = @_; $e->{sueldo} += $delta; } sub sueldo # método de acceso { my $e=shift; return $e->{sueldo}; } 1;
Ejemplo:
empleado2 |
#!/usr/bin/perl use Empleado2; $e1=new Empleado2("Juan Pérez", "3.141.159-2", 350000); $e2=new Empleado2("Pedro González", "2.718.281-8", 390000); print "Hay ", Empleado2->nemp, " empleados\n";
Los objetos dejan de existir al dejar de referirnos a ellos. Si se provee un método DESTROY, se ejecuta automáticamente en ese momento:
Empleado3.pm |
package Empleado3; my $nemp=0; sub new { my($pkg, $nombre, $rut, $sueldo) = @_; my $e = { nombre => $nombre, rut => $rut, sueldo => $sueldo }; ++$nemp; return bless $e, $pkg; } sub DESTROY { my $e=shift; print $e->{nombre}, " Q.E.P.D.\n"; --$nemp; } sub nemp { return $nemp; } sub impr { my $e=shift; print "Nombre: ", $e->{rut}, "\n", "RUT: ", $e->{nombre}, "\n", "Sueldo: ", $e->{sueldo}, "\n"; } sub aumenta_sueldo { my($e, $delta) = @_; $e->{sueldo} += $delta; } sub sueldo # método de acceso { my $e=shift; return $e->{sueldo}; } 1;
Ejemplo:
empleado3 |
#!/usr/bin/perl use Empleado3; $e1=new Empleado3("Juan Pérez", "3.141.159-2", 350000); $e2=new Empleado3("Pedro González", "2.718.281-8", 390000); print "Hay ", Empleado3->nemp, " empleados\n"; $e1=0; print "Hay ", Empleado3->nemp, " empleados\n";
Un método incluso podría haber sido llamado como
Clase->metodo
o bien
$objeto->metodo
y el siguiente trozo de código permite distinguir entre ambos casos:
sub metodo { my($r, ...) = @_; if(ref $r) { # fue llamado con un objeto (instancia) } else { # fue llamado para la clase } }
Empleado.pm |
package Empleado; sub alloc { my($pkg, $nombre, $rut) = @_; my $e = { nombre => $nombre, rut => $rut }; return bless $e, $pkg; } sub nombre { my $e=shift; return $e->{nombre}; } 1;
EmpJC.pm |
package EmpJC; @ISA=("Empleado"); sub new { my($pkg, $nombre, $rut, $sueldo) = @_; my $e=$pkg->alloc($nombre, $rut); # usamos al padre $e->{sueldo}=$sueldo; return $e; } sub sueldo { my $e=shift; return $e->{sueldo}; } 1;
EmpHoras.pm |
package EmpHoras; @ISA=("Empleado"); sub new { my($pkg, $nombre, $rut, $valor_hora, $num_horas) = @_; my $e=$pkg->alloc($nombre, $rut); $e->{valor_hora}=$valor_hora; $e->{num_horas}=$num_horas; } sub sueldo { my $e=shift; return $e->{num_horas} * $e->{valor_hora}; } sub pone_horas { my($e, $h) = @_; $e->{num_horas}=$h; } 1;
Para accesar explícitamente un método del padre se usa
$obj->SUPER::metodo;
Esto permitiria que alloc
se llamara new
y se usaria
$pkg->SUPER::new( ... );
Perl permite herencia múltiple, mediante:
@ISA=("padre1", "padre2"); # O bien @ISA=qw(padre1 padre2);
Todos las clases heredan de una clase llamada UNIVERSAL.
También existen los métodos isa
y can
:
Rectangulo->isa("Figura") # verdadero si Rectangulo hereda de Figura # directa o indirectamente Rectangulo->can("dibujar") # verdadero si Rectangulo o alguno de sus # ancestros tiene un metodo "dibujar"