|
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
$xal final de@a
$x = pop(@a)- Extrae el último elemento de
@a
unshift(@a,$x)- Agrega
$xal 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.
openopen 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 mapgrep(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"