Julio de 2010


Categorías

Inicio
Ayuntamiento
Entorno
Situación
Salón Social
Meteorología
Foro
Boletín
Tecnología
Fútbol
Turismo
Empresas
Fiestas
Asoc. Cultural
Patrimonio
Cultura
Visitas
Prensa
Cartelera
Anuncios DPH
Mercadillo

Citas del día:

Las antipatías violentas son siempre sospechosas y revelan una secreta afinidad. William Hazlitt. Escritor inglés.











La Puebla de Castro (www.lapuebladecastro.com)
www.lapuebladecastro.com > Tecnología > Programación > Iniciación a Perl


Apuntes: Introducción al lenguaje Perl



Éstos apuntes se corresponden con el material de partida de un curso de introducción al lenguaje Perl que confeccioné durante el año 2004. Pretenden ser una guía inicial al lenguaje y están pensados para ser estudiados a través de la realización práctica de los ejemplos expuestos. Si bien dichos ejemplos son en su mayor parte muy elementales, están pensados para poner de manifiesto las peculiaridades del lenguaje.


Índice



Introducción

Qué es Perl ?

Perl es un lenguaje de programación de propósito general originalmente enfocado hacia el tratamiento y manipulación de textos. Su nombre se corresponde con las siglas:

Practical Extraction and Report Language

A continuación figura la descripción de la página man original de la versión Perl 1.000 lanzada el 18 de Diciembre de 1987 por Larry Wall :

	

	NAME

		perl | Practical Extraction and Report Language

	SYNOPSIS

    	perl [options] filename args


	DESCRIPTION

     	Perl is a interpreted language optimized for scanning  arbi-
     	trary  text  files,  extracting  information from those text
     	files, and printing reports based on that information.  It's
     	also  a good language for many system management tasks.  The
     	language is intended to be practical  (easy  to  use,  effi-
     	cient,  complete)  rather  than  beautiful  (tiny,  elegant,
     	minimal).  It combines (in  the  author's  opinion,  anyway)
     	some  of the best features of C, sed, awk, and sh, so people
     	familiar with those languages should have little  difficulty
     	with  it.  (Language historians will also note some vestiges
     	of csh, Pascal, and  even  BASIC|PLUS.)   Expression  syntax
     	corresponds  quite  closely  to C expression syntax.  If you
     	have a problem that would ordinarily use sed or awk  or  sh,
     	but  it exceeds their capabilities or must run a little fas-
     	ter, and you don't want to write the silly thing in C,  then
     	perl  may  be  for  you.  There are also translators to turn
     	your sed and awk scripts  into  perl  scripts.   OK,  enough
     	hype.



Desde sus inicios, Perl fue concebido como un lenguaje orientado al desarrollo de herramientas de monitorización, extracción de información y elaboración de informes; no obstante, a día de hoy, el uso de Perl se ha extendido del mismo modo que sus prestaciones para ofrecer la posibilidad de soportar muchos tipos de aplicaciones destacando por ejemplo:

  • Administración de sistemas
  • Desarrollo Web
  • Programación en Red

Características de Perl

Perl es un lenguaje diseñado para ser interpretado, es decir, a diferencia de otros lenguajes como C o Fortran no requiere compilación del código por parte del usuario para poder ser ejecutado. Así pues, en la mayor parte de las ocasiones, el usuario utilizará un fichero de texto en el que almacenará una serie de directivas (i. e. un script) y a continuación ejecutará dicho script a través del intérprete de Perl :

	
C:\> perl script.pl


La inmediatez que proporciona el esquema de un lenguaje interpretado junto con las prestaciones que proporciona para tareas como el tratamiento de cadenas de texto y las comunicaciones por internet, la flexibilidad que aporta para tratar y estructurar grandes cantidades de información a través de una sintaxis flexible y elegante además de la existencia de una comunidad de usuarios muy extendida que aporta soluciones modulares a través de CPAN (Comprehensive Perl Archive Network -> http://www.cpan.org) han hecho de Perl uno de los lenguajes más atractivos a día de hoy para el desarrollo de todo tipo de aplicaciones.

Primeros pasos

Para empezar, podemos considerar la creación de nuestro primer script en Perl, mediante el uso de un editor de textos convencional (Notepad en Windows, Kwrite en Linux, etc ...) podemos editar un primer script :

	
     1	#!/usr/bin/perl
     2	print "Hola, Mundo !!\n";


Una vez guardado dicho script en un fichero con el nombre holamundo.pl ya podemos ejecutarlo invocando al intérprete del siguente modo:

	
C:\> perl holamundo.pl


Con lo que obtendremos el mensaje

	
Hola, Mundo !!


impreso en la consola del sistema.

La primera linea del script consiste en un comentario (los comentarios en perl van precedidos del carácter almohadilla -> #) que no tiene efectos sobre el flujo de ejecución del programa y en éste caso, indican al intérprete de comandos del sistema la ruta del intérprete empleado.

La segunda linea es la que contiene el código Perl y consiste en una llamada a la función print con el argumento entre comillas correspondiente a la cadena de texto que queremos mostrar por pantalla : "Hola, Mundo !!\n". Por último, la linea concluye con el punto y coma ; que indica al intérprete la conclusión de la directiva.

Fundamentos de Perl

Variables

Las variables en Perl representan espacios de memoria con un indicador o nombre asociado a través del cual podemos recuperar sus contenidos para operar con los datos que dichos espacios contienen. Las variables (escalares) en Perl se diferencian de otras piezas de código por ir precedidas del carácter $.

A modo de ejemplo tomemos el siguiente script:

	
     1 #!/usr/bin/perl
     2 print "Como te llamas ? ";
     3 $nombre = <STDIN>;
     4 print "Hola $nombre\n";


Las primeras dos lineas son análogas a las del anterior ejemplo holamundo.pl. En la linea 3, se cita la etiqueta asociada a la entrada por defecto STDIN (standard input) mediante el operador readline representado por los carácteres < > y lo que hace es almacenar la cadena de texto introducida por el usuario a través del teclado en la variable representada como $nombre dentro del contexto del script mediante el operador de asignación =. Por último se cita dicha variable en un contexto apropiado produciéndose un saludo personalizado.

Una de las características particulares de Perl es la existencia de una serie de variables cuyo contenido está predefinido en función del contexto en el que se citen y en algunos casos son pasadas como argumentos implícitos a funciones u operadores concretos.

Valores literales y sustitución

Literales numéricos

En el caso de hacer referencia a valores numéricos, uno puede escoger entre varias opciones:

	
# valor entero 
     $x = 12345;
# valor punto flotante
     $x = 12345.67;			
# notacion cientifica
     $x = 6.02e23;			
# uso de guion (legibilidad)
     $x = 4_287_283_209;
# valor octal 
     $x = 0377;
# hexadecimal
     $x = 0xfa6b;
# binario
     0b1100_0000;


Los valores numéricos pueden ser procesados en operaciones algebraicas comunes como la adición, sustracción, multiplicación y etc ... y además en caso de encontrarse en el contexto de una cadena de carácteres se sustituyen sin más por un valor legible cuyo formato de salida puede controlarse tal y como se verá más adelante.

Cadenas de texto

En el caso de tratar con cadenas de texto, existen numerosas posibilidades de inserción dentro del código. Lo más habitual consiste en delimitarlas mediante comillas normales :

	
     $nombre_y_apellido = "Bart Simpson";
     $nombre = "Bart";
     $apellido = "Simpson";
     $nombre_completo = "$nombre $apellido";


De éste modo, los nombres de variables se sustituyen por sus respectivos contenidos de forma adecuada ... igualmente, los carácteres especiales de tabulación -> \t, retorno de carro -> \n y etc ... también surten su efecto. En caso que exista la necesidad de expresar un carácter especial como $ dentro de una cadena, podemos hacerlo mediante el prefijo \ del siguiente modo:

	
     $precio = "150.0 $";


O de forma alternativa, hacer uso de los apóstrofes a modo de delimitadores que evitan cualquier tipo de sustitución:

	
     $precio = '150.0 $';


También existe la posibilidad de tratar una cadena de texto como un comando a ejecutar a través del intérprete de comandos correspondiente (DOS, Bash, Csh, ...). Para ello utilizaremos los delimitadores correspondientes al acento abierto ...

	
     1	#!/usr/bin/perl
     2	$txt = `dir`;
     3	print "$txt\n";


Así pues, en la segunda linea del ejemplo, se almacena en la variable $txt la salida del comando dir que contiene un listado de los archivos y directorios presentes en el directorio en el que nos encontremos en el momento de ejecutar el script. A continuación en la linea 3, dicha variable se muestra por pantalla.

En caso de tener que tratar con una cadena larga en la que existan saltos de carro, tabulaciones, símbolos especiales como los mismos delimitadores ", etc ... podemos hacer uso de la construcción print <<FINAL; que nos permite mostrar el contenido de la cadena que sigue hasta el momento en que se encuentra la la palabra FINAL. A modo de ejemplo podríamos implementar un script que inserte en un documento HTML el listado de contenidos del directorio actual:

	
     1	#!/usr/bin/perl
     2	$txt = `dir`;
     3	print <<FINAL_HTML;
     4	<html>
     5	<head>
     6		<title>Listado del directorio actual</title>
     7	</head>
     8	<body>
     9		<p align="center">$txt</p>
    10	</body>
    11	</html>
    12	FINAL_HTML
    13


Por último, hay que hacer notar que Perl proporcina la posibilidad de hacer uso de una notación alternativa para los delimitadores más comunes:

Notación común Alternativa Significado Sustitución
Apóstrofes q// Cadena literal No
Comillas qq// Cadena literal Si
Acentos abiertos qx// Ejecución Si

Según lo expuesto, podríamos decir que las siguientes construcciones son equivalentes. Cabe notar que en caso de hacer uso de la notación alternativa, los delimitadores son personalizables; se pueden usar indistintamente símbolos cualesquiera @@, (), !!, ?? etc ... :

	
     `dir $directorio_actual`
     qx!dir $directorio_actual!


Arrays

Si bien, en los ejemplos anteriores, se ha considerado exclusivamente un tipo de variable escalar capaz de contener un único valor numérico o cadena de texto cuyo nombre es precedido por el símbolo $; para representar una lista ordenada de valores se emplea un segundo tipo de variable denominado array. Para indicar que una variable es de tipo array se emplea el símbolo @ a modo de prefijo del mismo modo que el símbolo $ precedía a los nombres de variables escalares.

Para construir un array a partir de valores literales o contenidos de otras variables se puede emplear la siguiente construcción:

	
     @dias_laborables = ("Mon","Tue","Wed","Thu","Fri");


o alternativamente hacer uso de un tipo de delimitador similar a los expuestos en la sección anterior que se emplea en la construcción de arrays ahorrando algo de tecleo:

	
     @dias_laborables = qw(Mon Tue Wed Thu Fri);


En los dos casos, hemos construido un array que contiene las abreviaturas de los días de la semana, para poder utilizar dicho array con posterioridad se emplea la notación referida a continuación:

	
     $primer_dia_laborable = $dias_laborables[0];     # Mon
     $tercer_dia_laborable = $dias_laborables[2];     # Wed


Hay que hacer notar que para acceder a un elemento de un array se necesita citar el índice de dicho elemento (el índice debe estar contenido entre dos corchetes cuadrados []), dicho índice consiste en un valor entero que empezando desde 0 va hasta el número de elementos menos uno. Además, puesto que los valores almacenados en el array son siempre escalares, hay que hacer uso de el prefijo $ en el momento de requerir un elemento concreto.

Una de las particularidades de Perl a la hora de tratar con arrays es la posibilidad de utilizar índices negativos :

	
     $ultimo_dia_laborable = $dias_laborables[-1];     # Fri
     $penultimo_dia_laborable = $dias_laborables[-2];     # Thu


Además existen numerosas funciones capaces de facilitar enormemente la gestión de arrays en un script, a modo de ejemplo cabría destacar la función push :

	
     push(@dias_de_la_semana,@dias_laborables);
     push(@dias_de_la_semana, qw(Sat Sun) );


que acepta como primer argumento un array al que se pretende añadir al final el(los) elemento(s) del array(escalar) especificado como segundo argumento.

Es también de gran utilidad la función scalar que devuelve el número de elementos que contiene un array, o también la función shift que extrae y devuelve el primer elemento del array.

Hay que destacar que el paso de argumentos desde la linea de comandos al script se realiza a través de un array predefinido bajo el nombre: @ARGV (del inglés Argument Values). De éste modo, si generamos el script newdir.pl :

	

     1	#!/usr/bin/perl
     2	$dir = shift(@ARGV);
     3	$txt = `dir $dir`;
     4	print "$txt\n";



Y lo invocamos mediante:

	

C:\> perl newdir.pl C:\usuarios\



Obtendremos en pantalla el listado de contenidos del directorio pasado como argumento al script.

En Perl, además de funciones específicas para la manipulación de arrays, existen también funciones cuyo valor de retorno es un array. Por ejemplo podríamos citar la función split cuyo primer argumento representa un carácter o combinación de carácteres mediante los cuales se requiere el troceado de una cadena de texto para posteriormente devolver la lista o array asociada al contenido diseccionado:

	
     1	#!/usr/bin/perl
     2	@dir = split(/\s+/,`dir`);
     3	print "$dir[3]\n";


En el ejemplo, se puede apreciar como en la segunda linea se invoca a la función split haciendo uso de la construcción /\s+/ como primer argumento para hacer constar que nos interesa trocear todas aquellas partes de la cadena que estén separadas por uno o más (modificador +) espacios en blanco (notado \s). La cadena a considerar se recoge como segundo argumento y sería la salida correspondiente al comando del sistema dir. Por último, se muestra por pantalla el nombre del cuarto archivo contenido en el directorio.

La función join podría considerarse como la inversa de split pues lo que hace es devolver una cadena escalar que contiene el conjunto de términos de un array separados por una cadena de texto especificable:

	

     $dir = join(",",@dir);



de éste modo obtendríamos un escalar $dir que albergaría una cadena de texto en la figurarían los valores contenidos en @dir separados por comas.

Llegados a este punto y aprovechando el ejemplo anterior, convendría mencionar el hecho de que una variable de un tipo (p. ej. escalar) y otra de otro tipo (p. ej. array) pueden tener el mismo nombre sin que entre ellas se produzca interferencia alguna. Dicho de otro modo, la variable escalar $dir y el array @dir ocupan lugares independientes y separados en la tabla de símbolos que el intérprete utiliza para procesar las directivas.

Hashes

Los hashes también conocidos como arrays asociativos constituyen el tercer tipo fundamental de variables con las que Perl puede trabajar. Del mismo modo que un array, un hash es una variable susceptible de contener toda una colección de valores. La diferencia con respecto a un array radica en el modo de indexar sus elementos; es decir, en ocasiones, puede ser interesante prescindir de una indexación numérica en detrimento de una indexación basada en etiquetas constituidas por cadenas de carácteres; el hash es el tipo de variable que nos proporciona ese tipo de indexación. Para hacernos una idea consideremos el ejemplo de asignación de un hash:

	
   %color_de = qw(manzana rojo pera verde);


Hasta aquí, la única diferencia apreciable con respecto de los ejemplos en los que construiamos arrays a partir de valores literales consiste en que un hash tiene un prefijo % en contraposición con los arrays (@) o los escalares ($).

Es interesante recalcar un detalle que se pone de manifiesto a través del ejemplo y que consiste en el hecho de que la parte derecha de la asignación es idéntica a la utilizada en ejemplos anteriores para la instanciación de un array; no obstante y dado que la parte izquierda contiene el nombre de un hash, el intérprete construye un hash puesto que Perl es extremadamente sensible al contexto en el que se situan determinadas expresiones o piezas de código. Dicha dependencia del contexto contribuye facilitar el desarrollo de un estilo de programación más compacto y que hace de Perl un lenguaje de programación mucho más próximo y potente acercándolo al lenguaje común que empleamos para comunicarnos de forma cotidiana a su vez tremendamente proteico y sensible al contexto en el que se utiliza; más adelante, se expondrá con más detalle ésta característica.

No obstante, el modo en que Perl nos da acceso a los elementos de un hash es totalmente distinto. Para apreciarlo basta el siguiente ejemplo:

	
   $fruta = "manzana";
   print qq@El color de una $fruta es $color_de{$fruta}\n@;
   $fruta = "pera";
   print qq@El color de una $fruta es $color_de{$fruta}\n@;


La salida por pantalla del script resultaría ser:

	
  C:\>perl color.pl
  El color de una manzana es rojo
  El color de una pera es verde


Así pues, parece que los literales manzana y pera especificados en la asignación del hash han sido considerados como las etiquetas de indexado (de ahora en adelante keys o claves del hash enmarcadas por corchetes {} en lugar de corchetes cuadrados [] como en el caso de los índices de un array) asociadas a los elementos del hash rojo y verde (valores del hash). Con objeto de diferenciar claramente etiquetas de valores en la instanciación de un hash, se suele emplear el operador => que podría considerarse como un sinónimo de la coma en construcciones del estilo de:

	
   %color_de = ("manzana" => "rojo","pera" => "verde");


Al igual que en el caso de los arrays, existen diversas funciones incorporadas a Perl que operan con hashes. Por ejemplo podríamos considerar la función keys que devuelve un array que contiene el conjunto de claves del hash:

	
   @frutas = keys %color_de;


del mismo modo, podemos recuperar los valores de un hash mediante la función values:

	
   @colores = values %color_de;


Hay que destacar que el orden en el que se disponen los elementos del array resultante en uno u otro ejemplo no tiene por que coincidir con el orden explicitado en la construcción del hash. Dependiendo de la implementación concreta de Perl que se esté utilizando, el orden puede variar en función del tipo de algoritmos de recuperación de valores o claves empleado; para garantizar la integridad a tal efecto y evitarnos sorpresas desagradables, lo más apropiado suele ser recurrir a una función que opera sobre arrays llamada sort que proporciona un array con los mismos elementos que el original reordenados alfabéticamente, en éste caso:

	
   @frutas = sort keys %color_de;


Al igual que en el caso del array predefinido @ARGV, también existe por ejemplo un hash predefinido llamado %ENV (del inglés environment) en el que se albergan las variables de entorno del sistema en el momento de ejecutarse el script:

	
     1	#!/usr/bin/perl
     2	print "USUARIO : $ENV{USER}\n";
     3	print "HOSTNAME : $ENV{HOSTNAME}\n";
     4	print "SHELL : $ENV{SHELL}\n";
     5	print "TERM : $ENV{TERM}\n";
     6	print "PWD : $ENV{PWD}\n";
     7	print "HTTP_PROXY : $ENV{HTTP_PROXY}\n";


El script anterior produciría una salida como la que sigue:

	
C:\>perl muestra_entorno.pl
USUARIO : pepito
HOSTNAME : isingx.isabenax.es
SHELL : /bin/bash
TERM : xterm
PWD : /home/pepito/cursos/perl/tex/ejemplos
HTTP_PROXY : http://pepito:correo@proxy.isabenax.es:8080


Operadores

Precedencia, asociatividad, etc ...

En Perl, existe un gran número de operadores distintos que proporcionan un grado de flexibilidad considerable a la hora de implementar cualquier conjunto de directivas. Dichos operadores se pueden clasificar en función del número de elementos sobre los que operan: unarios, binarios, trinarios, ... además del tipo de elementos sobre los que actuan: cadenas, valores numéricos, referencias, ... o su precedencia y también asociatividad relacionada con el orden en el que evalua sus operandos: p. ej. primero el de la izda. o viceversa, etc ...

De hecho, un operador puede ser considerado como una función del lenguaje que tiene una expresión sintáctica especial. En realidad, podemos considerar como operadores, aquellas funciones que llamamos sin hacer uso de paréntesis para enmarcar los argumentos, por ejemplo:

	
     1 #!/usr/bin/perl
     2 chdir 'C:\usuarios\' || die 'Error de acceso a C:\usuarios\';
     3 $txt = `dir`;
     4 print "$txt";
 


En éste caso, hacemos uso de la instrucción chdir a modo de operador que sirve para ubicarnos en un directorio concreto; la expresión chdir TERMINO cambia el directorio en el que trabaja el script y devuelve un 1 en caso de que la operación se complete con éxito y un 0 en caso contrario; igualmente podríamos realizar la llamada del siguiente modo:

	
     chdir('C:\usuarios\') || die 'Error de acceso a C:\usuarios\';


En ambos casos, encontramos el operador || que representa a la operación lógica OR y según lo expuesto la directiva podría leerse del siguiente modo:

  • i) La directiva, es una operación lógica OR || entre dos términos
  • ii) Puesto que la asociatividad de || es tal que el primer término en ser evaluado es el de la izda. se procesa la pieza chdir que efectua la tentativa de cambio de directorio actual. En caso de éxito, devuelve 1 resolviendo la operación lógica y concluyendo el proceso.
  • iii) En caso de que la operación fallase debido a la no existencia del directorio o a la falta de permisos del usuario, la pieza chdir devolvería un 0 y esta situación requeriría la evaluación del operando a la derecha de la función lógica. Dicho operando es una instrucción que detiene la ejecución del programa i.e. die mostrando un mensaje a modo de justificación.

La diferencia entre las dos formas de expresar la directiva consisten en que en el segundo caso, hacemos explícito el hecho de que nuestro propósito es que el intérprete lea la directiva en el orden adecuado; en cambio, en el primer caso, no explicitamos ningún tipo de orden pues las reglas de precedencia de los operadores || y chdir establecen un orden implícito que en éste caso coincide con nuestras pretensiones (ver Figuras adjuntas)

Orden de evaluación implícito basado en las reglas de precedencia Orden incorrecto de evaluación de la expresión

Dado que en Perl, existe una gran cantidad de operadores con muy diversas funciones, también existe una gran colección de reglas de precedencia y asociatividad; normalmente dichas reglas suelen encajar con el sentido común de la mayoría de los mortales y además en muchos casos se establecen en función de la comodidad de uso. De todos modos, en caso de dudas con respecto a la precedencia de tal o cual operador, suele ser aconsejable consultar las especificaciones y en cualquier caso, forzar la precedencia deseada mediante el uso de los paréntesis.

Operadores de uso común

A continuación, se recopila una serie de descripciones de operadores de uso cotidiano junto con ejemplos relacionados.

En lo que sigue se ha obviado la descripción de operadores matemáticos presentes en Perl como *, +, -, \% y etc ... puesto que su uso es muy generalizado en aplicaciones como Excel y lenguajes como C, Fortran o Java

  • Auto(incremento - decremento) -> Se expresan mediante ++ o -- respectivamente, y tienen el efecto de incrementar o decrementar en una unidad el resultado de la expresion a la que se aplican; dependiendo de si se emplean a modo de prefijo o sufijo, tienen efectos diferentes;

    	
         print ++($numero = 99); # imprime 100
         print $numero++; # imprime 99, fija a 100 $numero
         	


  • Operador multiplicativo x -> A diferencia de la multiplicación algebraica *, x debe ser entendido como un operador de repetición mediante el cual podemos mostrar 80 guiones o también anular todos los elementos de un array :

    	
         print "-" x 80;
         @array = (0) x scalar @array;
    	


  • Operador aditivo . -> Dicho operador sirve para concatenar distintas expresiones:

    	
         $var = "Bart "."Simpson"; # se asigna a $var  "Bart Simpson"
    	


  • Condicional trinario ? : -> Dicho operador necesita tres términos para ser procesado, el primero (a la izquierda del interrogante) representa una condición que puede o no satisfacerse. En caso satisfactorio se procesa la pieza de código que precede a los dos puntos; en caso contrario, se procesa el término situado a la derecha de los dos puntos:

    	
         (1 > 2) ? print "Imposible !!" : print "Falso, 2 > 1 !!!";
         	


  • Testeo de ficheros -> Perl nos proporciona más de 20 operadores para recuperar información acerca de ficheros o directorios presentes en nuestro sistema. De entre ellos, cabe destacar el operador -e que devuelve 1 en caso de que el fichero especificado por la expresión que le siga exista, 0 en caso contrario:

    	
         -e $fichero ? unlink $fichero : ();
    	


    De éste modo, eliminamos el fichero apuntado por la cadena contenida en la variable $fichero y en caso de que no exista no efectuamos ninguna operación emplazando un término vacío () cómo último operando. Para comprobar si un fichero es un directorio podemos utilizar el operador -d tal y como se ilustra en el siguiente ejemplo:

    	
         (-e $dir) && (-d $dir) ? chdir $dir : ();
    	


    En éste caso se utiliza el operador lógico AND expresado mediante && para testear la validez del directorio apuntado por $dir antes de proceder al cambio de directorio. También se suelen utilizar de forma asidua los operadores -M y -A que devuelven respectivamente el número de días que han transcurrido desde la última modificación o acceso a un fichero concreto y el tiempo de inicio de interpretación del script:

    	
         print "$file fue modificado hace ".(-M $file)." dias\n";
    	


  • Operadores comparativos -> Se emplean para efectuar comparaciones entre sus operandos, tenemos entre ellos el operador == que devuelve 1 en caso de que se comparen dos valores numéricos iguales y 0 en caso contrario. El operador != que consistiría en testeo de desigualdad numérica:

    	
         $a != 0 ? $b = 100.0/$a : print "Division por 0 !!\n";
    	


    En el caso de cadenas de texto, los operadores análogos a == y != se representan mediante eq (equal) y ne (not equal).

  • Operador rango .. -> Se utiliza para construir un rango de valores:

    	
         @ultimos_cinco_colores = @colores[-5..-1];
         @alfabeto = ('A'..'Z');
    	


  • Operadores de asignación -> A modo de ejemplo utilizado extensivamente hasta el momento podríamos citar el operador =, también podemos emplear algunas variantes como +=, .=, ...

    	
         $numero = 100.0;
         $numero += 50.0; # $numero contiene 150.0
         $cadena = "El precio es de ";
         $cadena .= $numero;
         $cadena .= " $"; # El precio es de 150.0 $
    	


Contextos

Tal y como se ha reflejado en alguno de los ejemplos expuestos hasta el momento, la forma que tiene el intérprete de Perl de procesar una determinada pieza de código depende en ocasiones del contexto en el que se encuentre.

Contexto escalar y contexto de lista

Constituyen los principales contextos en los que se evaluan la mayoría de expresiones en Perl. En el caso de que por ejempo requiramos una asignación situando a la izquierda un escalar tal y como una variable escalar, un elemento concreto de un array o un hash, el intérprete atribuye automáticamente un contexto escalar a la parte derecha de la asignación:

	
     $x = funcion();
     $x[1] = funcion();
     $x{"manzana"} = funcion();


En caso de que en la parte izda. de la asignación figure un array, hash, etc ... la parte dcha. se evaluará en un contexto de lista:

	
     @x = funcion();


Igualmente, podemos atribuir una interpretación en contexto de lista a asignaciones del tipo:

	
     ($x,$y,$z) = funcion();
     ($x) = funcion("X");


En los ejemplos se ha empleado una función hipotética denominada funcion que es capaz de saber en que contexto se está evaluando devolviendo un escalar o una lista en consecuencia. Se puede decir que la función funcion() está sobrecargada en el tipo de retorno. Del mismo modo que las combinaciones del operador de asignación citadas pueden proveer de diferentes contextos a sus operandos, cualquier operador en Perl proporciona un determinado contexto en función del modo en que se utilize. Normalmente dicha contextualización se establece de forma bastante intuitiva pero en caso de duda siempre resulta recomendable consultar las especificaciones.

Contexto booleano

En algunos casos, existen términos que se evaluan en lo que se conoce como contexto booleano, es decir en aquellos casos en los que se requiera un valor falso o verdadero. En caso de que un escalar sea evaluado en dicho contexto existen varias posibilidades:

 VerdaderoFalso
Escalar numérico≠ 0= 0
Cadena de textone ''eq ''

Para el caso de los arrays no existe un contexto booleano tal como el de los escalares, no obstante, podemos emplear un array en una decisión tal y como sigue:

	
     (@array) ? print "$array[0]\n" : print "Array vacio\n";


Puesto que dado que @array está siendo evaluado en contexto escalar, Perl interpreta el término como un escalar y en éste caso se produce una llamada implícita a la función scalar que proporciona el número de elementos de un array (Cualquier array @array citado en contexto escalar es equivalente a la expresión scalar @array proporcionándonos un esquema bastante afortunado):

 VerdaderoFalso
ArrayNo vacíoVacío

Variables predefinidas

A continuación se va a describir brevemente la función de algunas de las variables que Perl utiliza por defecto para realizar determinadas tareas. En la página perlvar del manual, se pueden consultar de un modo más exhaustivo todas las variables obviadas en ésta sección.

Escalares

  • $_ -> Se corresponde con la variable de entrada por defecto y en caso de omisión, es la que se pasa como argumento a las funciones print, chomp, etc ..
  • $a, $b -> Se emplean para adecuar los criterios de la función sort y representan dos términos de una lista a ordenar entre los cuales se establece el criterio de ordenación.
  • $. En ella encontramos el número de linea en el que se encuentra el proceso de lectura del último fichero accedido.
  • $/ -> En ella se almacena una cadena de carácteres a considerar como separador de lineas en operaciones de lectura, por defecto se fija a \n.
  • $¡ -> Si se fija a un valor distinto de cero, fuerza las operaciones de escritura a producirse en el momento de procesamiento de las directivas correspondientes eliminando el paso de lineas por el buffer de salida. Suele ser de utilidad para establecer comunicaciones entre procesos a través de sockets o pipes.
  • $$ -> Nos proporciona el PID del intérprete que ejecuta nuestro script. Dicho valor suele ser de sólo lectura aunque puede ser alterado mediante la instrucción fork().
  • $0 -> Contiene el nombre del script en ejecución
  • $] -> Contiene información acerca de la versión de la distribución de Perl empleada para ejecutar el script.

Arrays y Hashes

Tal y como fueron descritas con anterioridad, las variables @ARGV y %ENV contienen respectivamente los argumentos pasados al script mediante la linea de comandos y la tabla de variables de entorno vigente en el momento de ejecutar el script.

Entrada y salida

Entrada y salida STD

Para empezar a tratar con el tema de la entrada y salida en Perl, resulta conveniente destacar que cualquier operación de ésta índole en Perl hace referencia implícita o explícitamente a una etiqueta de fichero también conocida como FILEHANDLE. Para evitar confusiones con etiquetas de fichero y otras variables o instrucciones, se suele emplear el convenio de destacar en mayúsculas cualquier filehandle.

Existen tres filehandles que podemos usar por defecto sin necesidad de realizar ninguna operación de apertura. éstos son el STDIN para entrada por teclado, STDOUT para salida por pantalla y el STDERR para salida por pantalla excepcional debido a un error, etc ...

	
     1 #!/usr/bin/perl
     2 $nombre = <STDIN>;
     3 chomp($nombre);
     4 $nombre ?
          print STDOUT "Hola $nombre\n" : print STDERR "Error !\n";


Así pues, en el ejemplo se puede apreciar como se hace uso del operador <> readline sobre la etiqueta correspondiente a la entrada por teclado STDIN y por lo tanto el script espera a que el usuario inserte su nombre. A continuación la instrucción chomp recorta el retorno de carro introducido por el usuario y toma la decisión de saludar al usuario por la salida de pantalla STDOUT en caso de que haya escrito una cadena no vacía o notificar un error por la salida de pantalla excepcional STDERR en caso contrario. En cualquier caso, podríamos haber obviado las etiquetas STDIN y STDOUT puesto que se consideran entradas y salidas por defecto y haber escrito $nombre = <>; ó print $nombre."\n";

Lectura y escritura de ficheros

La instrucciones open y close nos permiten abrir y cerrar ficheros con el propósito de leer o escribir datos. Su sintaxis es la que se ilustra a continuación:

	
     1  #!/usr/bin/perl
     2  open(OUT,">archivo.txt"); # prefijo > (creacion y escritura)
     3  print OUT "Esta es la primera linea de archivo.txt\n";
     4  print OUT "Esta es la segunda linea de archivo.txt\n";
     5  close(OUT);
     6  open(TXT,"archivo.txt"); # apertura en modo de lectura
     7  $primera_Linea = <TXT>;
     8  chomp($primera_Linea);
     9  print $primera_Linea."\n";
     10 $segunda_Linea = <TXT>;
     11 close(TXT);


En la segunda linea se requiere la apertura de un fichero para operaciones de escritura (notar el prefijo >) llamado archivo.txt. El hecho de hacer constar dicho prefijo inmediatamente antes del nombre del fichero indica que en caso de que el fichero exista, éste será eliminado y creado de nuevo. En la sexta linea se abre el archivo previamente creado sin incluir ningún prefijo y por lo tanto Perl entiende que deseamos realizar operaciones de lectura (modo de apertura por defecto). En el ejemplo también se hace uso del operador readline <> que usado en contexto escalar devuelve sucesivamente cada una de las lineas del fichero a medida en que se invoca. En contexto de lista, el operador <> devuelve la totalidad de lineas del fichero en forma de array:

	

     1	#!/usr/bin/perl
     2	$archivo = "ejemplos/holamundo.pl";
     3	open(TEX,$archivo) || die "No puedo abrir $archivo\n";
     4	@array = <TEX>;
     5	close(TEX);
     6	@array ? print join("",@array)."\n" : die "$archivo vacio\n";


Existen otras formas de abrir ficheros como por ejemplo en modo append que se utiliza para escribir lineas al final de un fichero ya existente sin eliminar su contenido anterior. Dicho modo de escritura se requiere mediante el prefijo >> antepuesto al nombre del fichero.

Del mismo modo que podemos realizar operaciones de apertura de ficheros, también podemos abrir procesos a modo de pipes, por ejemplo, podemos abrir una aplicación cualquiera para interactuar con ella:

		

     1	#!/usr/bin/perl
     2	open(OCT,"|octave");
     3	print OCT "2+3";
     4	close(OCT);


En éste caso, abrimos la aplicación octave (Aplicación GNU similar a Matlab) mediante el prefijo pipe indicando de ese modo que nuestro propósito no es abrir un archivo llamado octave sinó comunicarnos con una instancia de la aplicación octave. A continuación imprimimos en la consola de la aplicación la operación requerida (en éste caso 2+3). octave la procesa y obtenemos la siguiente respuesta por pantalla:

		     

[alfonso@isingx tex]$ perl ejemplos/pipa.pl

GNU Octave, version 2.1.40 (i386-redhat-linux-gnu).
Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001, 2002 John W. Eaton.
This is free software; see the source code for copying conditions.
There is ABSOLUTELY NO WARRANTY; not even for MERCHANTIBILITY or
FITNESS FOR A PARTICULAR PURPOSE.  For details, type `warranty'.

Please contribute if you find this software useful.
For more information, visit http://www.octave.org/help-wanted.html

Report bugs to <bug-octave@bevo.che.wisc.edu>.

ans = 5


Tal y como se puede apreciar, en la última linea de la salida obtenemos la respuesta (answer) = 5.

Expresiones regulares

La gestión de expresiones regulares en Perl constituye una de las más potentes herramientas para el tratamiento y la gestión de cadenas de texto más o menos estructuradas. En ésta sección se expone una introducción a su modo de uso junto con algunos ejemplos que permitan anticipar el tremendo potencial que proporcionan.

Coincidencia simple de palabras

Una cadena de carácteres literal podría considerarse como la expresión regular más simple:

		
     "Hola Mundo" =~ m/Mundo/;     # Coincidencia !!


La novedad del ejemplo consiste en el uso del operador ~ que se emplea para relacionar una expresión con una expresión regular o regex. En este caso, la pieza de código retornaría un valor cierto en caso de que la expresión a la izda. del operador contuviera en algún punto la secuencia expresada literalmente en la regex. La letra m que precede al delimitador / procede del inglés match -> coincide ...

		
     $cadena !~ m/Mundo/ ? () : print "Coincidencia\n";


En éste caso se ha empleado el operador !~ que constituye el complementario del anterior en un condicional en el que se imprime un mensaje en caso de que exista coincidencia entre el contenido de la cadena de carácteres $cadena y la palabra Mundo. Dada la naturaleza reservada de los carácteres:

		
     {} [] () ^ $ . │ * + ? \


Necesitaremos la presencia de un backslash \ precediéndolos para poder hacerlos constar de forma explícita dentro de una regex. Por otro lado, carácteres ASCII especiales pueden explicitarse haciendo uso de secuencias comunes como:

		
     \t     \n     \s     \r     \a


Bytes arbitrarios pueden ser especificados mediante por secuencias octales o hexadecimales:

		
     "cat" =~ m/\143\x61\x74/            # Coincide !!!!


Hay que recalcar que dentro de una regex, podemos incluir nombres de variables pues Perl interpreta su contenido de un modo similar al de las expresiones enmarcadas entre comillas.

Uso de clases de carácteres

El uso de clases de carácteres permite incluir en una regex un conjunto determinado de combinaciones:

		
     "cata" =~ m/[bcr]ata/;     # Coincide
     "bata" =~ m/[bcr]ata/;     # Coincide
     "rata" =~ m/[bcr]ata/;     # Coincide 
     m/[yY][eE][sS]/;	# Coincide con yes, YES, yES y etc ...


Tal y como se puede apreciar en el último ejemplo, en muchas ocasiones convendrá que una regex se evalue sin distinguir letras mayúsculas de minúsculas, a tal efecto, se puede posponer un modificador i -> (del inglés case insensitive) después del último delimitador:

		
     m/yes/i;


Los carácteres reservados de una clase de carácteres son distintos de aquellos que se emplean fuera y son los siguientes:

		
     - ] \ ^ $


El carácter $-$ actua como un operador de rango de modo que:

		
     [0123456789] = [0-9]
     [abc........xyz] = [a-z]
     [0-9a-fA-F] -> numero hexadecimal


Si al principio de la clase de carácteres emplazamos un acento circunflejo, obtenemos el efecto de negar la clase de carácteres, es decir, coinciden todos los carácteres exceptuando los contenidos en la clase:

		
     m/[^a]ata/;     # Coincidencia con bata pero no con aata o ata


Perl proporciona algunas abreviaciones para clases de carácteres de uso común:

		
     \d = [0-9]
     \s = [\ \t\r\n\f]
     \w = [0-9a-zA-Z]
     \D = [^\d]
     \W = [^\w]
     . = [^\n]


Las abreviaciones \d, \s, \w, \D, \S y \W pueden ser empleadas dentro de una clase de carácteres:

		
     m/\d\d:\d\d:\d\d/;     # Coincide con el formato horario hh:mm:ss
     m/fin\./;   # Coincide con fin.
     m/fin[.]/;  # Lo mismo.


La secuencia \b -> word boundary coincide en la posición en la que exista un carácter alfanumérico precedido o seguido de uno que no lo es:

		
     $x =~ m/\bcata\b/i;    # Ni con catadura ni con bocata


Por último hay que destacar que el acento circunflejo expresado al inicio de una regex y fuera de una clase de carácteres [ ], representa el inicio de la cadena y el metacarácter $ su final:

		
     /^Erase\suna\svez/;
     /comieron\sperdices$/;


Alternativas

Existe un metacarácter de alternación (pipe) que se puede utilizar para forzar coincidencias entre varias alternativas posibles:

		
     "perros y gatos" =~ m/gato|perro|pato/; # Coincide con perro


Tal y como se puede apreciar en el ejemplo, la coincidencia se produce con perro pues es la primera secuencia coincidente que aparece en la cadena literal a la derecha del operador.

Agrupación

Los paréntesis se pueden emplear para agrupar un conjunto de elementos dentro de una regex:

		
     $palabra =~ m/cas(ero|a|ita|eta)/; # Casero, casa, casita o caseta


Extracción

Los metacarácteres de agrupación (i. e. paréntesis) permiten la extracción de la cadena coincidente con su contenido. Los valores obtenidos por orden de derecha a izquierda dentro de una regex, se pueden recuperar mediante las variables especiales $1, $2, $3 y etc ...

		
     $tiempo = "17:20:18";
     $tiempo =~ m/(\d\d):(\d\d):(\d\d)/;
     ($hora, $minuto, $segundo) = ($1, $2, $3);


En contexto de lista, una regex coincidente con agrupaciones, devolverá la lista de los valores coincidentes:

		
    $t = "17:21:16";
    ($hora, $minuto, $segundo) = ($t =~ m/(\d\d):(\d\d):(\d\d)/);


Los agrupaciones en una regex se pueden anidar, de modo que el contenido de $1 se corresponderá con la coincidencia del primer paréntesis abierto por la izda. en el caso de $2 trataremos con el contenido del segundo paréntesis abierto igualmente por la izda. y así sucesivamente tal y como se ilustra a continuación:

		
     m/(ab(cd|ef)((gi)|j))/;
        1  2     34


Dentro de una regex se pueden recuperar variables extraidas previamente mediante el uso de las referencias (backreferences) \1, \2, ... de modo que:

		
     m/(\w\w\w)\s\1/;


Coincidiría con cadenas en las que existan secuencias de tres carácteres alfanuméricos idénticos y dispuestos en el mismo orden separados por un espacio.

Iteradores

En muchos casos, nos interesa cuantificar el número de veces en el que se debe producir una secuencia para coincidir dentro de una regex. A tal efecto deben emplearse los metacarácteres:

		
     * + ? {}


Su uso se ilustra a continuación:

  • a? -> coincide con 'a' 1 o 0 veces
  • a* -> coincide con 'a' 0 o más veces (Cualquier número de veces)
  • a+ -> coincide con 'a' 1 o más veces (Al menos una vez)
  • a{n,m} -> coincide con 'a' entre n y m veces
  • a{n,} -> coincide con 'a' un mínimo de n veces
  • a{n} -> coincide con 'a' repetida n veces

Modificadores

Hasta ahora hemos visto el modificador //i que se utiliza para establecer coindencias case insensitive. Existen otros modificadores como //o que realizan tan sólo una vez las sustituciones de variables en una regex. Se trata de una optimización pues antes de procesar la regex, el intérprete de Perl llama a un compilador de expresiones regulares. En caso de que una regex permanezca invariable en varias llamadas sucesivas; haciendo uso del modificador //o evitamos la utilización innecesaria de dicho compilador.

El modificador //g permite realizar una búsqueda de coincidencias a lo largo de una cadena pues coincide tantas veces cómo sea posible a lo largo de una cadena. En contexto escalar se realiza un salto a la siguiente coincidencia para cada operador =$sim$ empleado. La posición de la cadena coincidente en cada caso se puede obtener gracias a la función pos():

		
     $x = "gato perro caseta";
     $x =~ m/(\w+)/g;
     print "La palabra $1, termina en la pos.",pos $x,"\n";
     $x =~ m/(\w+)/g;
     print "La palabra $1, termina en la pos.",pos $x,"\n";
     $x =~ m/(\w+)/g;
     print "La palabra $1, termina en la pos.",pos $x,"\n";


La salida del ejemplo resultaría como sigue:

		
La palabra gato, termina en la pos.4
La palabra perro, termina en la pos.10
La palabra caseta, termina en la pos.17


En caso de que la regex con el modificador //g se evalue en un contexto de lista, se devuelve la lista de secuencias coincidentes del siguiente modo:

		
     $x = "gato perro caseta";
     @x = ( $x =~ m/(\w+)/g );
     print join(",",@x)."\n"; # imprime : gato,perro,caseta


Ejemplo -> Un parser de URLs

Gracias a lo expuesto hasta el momento, podríamos considerar el ejemplo de un parser de URLs ... una URL es más o menos una dirección de internet, como por ejemplo : http://www.google.com/webmasters/sitemaps, se trata de una cadena de texto en la que uno especifica un protocolo de comunicaciones, en éste caso el HyperText Transfer Protocol -> http, el nombre de un host (en éste caso el de Google) y además la ruta hacia el contenido que se desea explorar: para el ejemplo se trataría de (/webmasters/sitemaps). Cualquier navegador como por ejemplo Internet Explorer o Mozilla lo primero que debe hacer para poder recuperar los contenidos de la página, es decodificar la URL que el usuario introduce en la casilla a tal efecto. Dicha operación en Perl podría resumirse en una sóla linea:

		
     1	#!/usr/bin/perl
     2	$URL = <STDIN>;
     3 print "\n$URL";
     4 ($protocolo,$servidor,$puerto,$path) =

     5 $URL =~ m@(\w+)*://([^/:]+):*(\d*)?([^#]*)@;

     6 $protocolo ? 
     7     print "Protocolo = $protocolo\n" : ();
     8 $servidor ? 
     9     print "Servidor = $servidor\n" : ();
    10 $puerto ? 
    11     print "Puerto = $puerto\n" : ();
    12 $path ? 
    13     print "Path = $path\n" : ();


En éste caso, la expresión regular se encuentra en la linea 5 y puede ser un buen ejercicio tratar de leerla teniendo en cuenta todo lo expuesto hasta ahora. A modo de ejemplos de salida del script podemos considerar:

		
http://www.ub.es/fis/ecm/asignaturas.html
Protocolo = http
Servidor = www.ub.es
Path = /fis/ecm/asignaturas.html

http://www.yahoo.es/deportes/index.html
Protocolo = http
Servidor = www.yahoo.es
Path = /deportes/index.html

ftp://ftp.upc.es:80
Protocolo = ftp
Servidor = ftp.upc.es
Puerto = 80


Búsqueda y sustitución

Hasta ahora, todas nuestras expresiones regulares venían acompañadas del prefijo m//. Existe otro modo de uso de una regex con el objeto de conseguir sustituciones de palabras, consiste en utilizar el prefijo s/// (s -> substitution) del siguiente modo:

		

     $guion = 

         "Vilma y Pedro Picapiedra viven una crisis matrimonial ...";

     $guion =~ s/Picapiedra/Simpson/gi;

     $guion =~ s/Pedro/Homer/gi;

     $guion =~ s/Vilma/Marge/gi;

     print "$guion\n";



produciéndose la salida:

		

Marge y Homer Simpson viven una crisis matrimonial ...



Una manera fácil de hacer remakes ?

El operador split

El operador split citado en la sección que hacía referencia a los arrays toma como primer argumento una regex, así pues, por ejemplo podemos servirnos de él para separar la siguiente lista de números separados por comas:

		

     $linea = "1.618,27,	   3.142";

     @linea = split /,\s*/, $linea;

     print "@linea\n"; # imprime 1.618 27 3.142



Directivas de Control del flujo y Visibilidad

Hasta ahora no se ha descrito la forma en que podemos regular el flujo de ejecución de un script orientado a la toma de decisiones que implique un bloque de código, ni a la forma de efectuar operaciones repetitivas y/o secuenciales y tampoco se ha tratado el tema de la visibilidad o scoping de las variables. El objeto de ésta sección es el de familiarizarnos con éstos tópicos desde un perspectiva Perl.

Condiciones

Con anterioridad se ha descrito el modo de empleo del operador condicional trinario A ? B : C gracias a él podemos derivar el flujo de ejecución de un script entre dos directivas alternativas B,C en función del cumplimiento de la condición A. En caso de que las alternativas B,C estén formadas por un conjunto más o menos complejo de directivas (i.e. bloque de código) es necesario recurrir a las directivas if - else - unless:

		

     if ( (-e $dir) && (-d $dir) ) {

          chdir $dir; 

     } else {

          unless (-e $dir)  {

               print STDERR "El directorio $dir no existe\n";

          } else {

               print STDERR "$dir no es un directorio\n";

          }

     }



Bucles

A continuación se recogen instrucciones relacionadas con el control de flujo de operaciones iterativas.

while - until

Se emplean para realizar operaciones iterativas siempre y cuando se cumpla la condición explicitada en la directiva while; unless funciona de modo similar pero en éste caso la iteración se produce hasta que la condición expresada se cumple:

		

    1 #!/usr/bin/perl

    2 @malsonantes = qw(porras corcholis mecachis);

    3 $malsonantes = join("|",@malsonantes);

    4 while ( chomp( $texto = <STDIN>) ) {

    5     ( @malsonantes = $texto =~ m/($malsonantes)/gi ) ? 

    6          print "Es feo decir \"@malsonantes\"\n" : ();

    7 }



for

El bucle for como casi todo en Perl, puede emplearse de distintos modos, por ejemplo, podemos iterar sobre los elementos de un array de modo sucesivo mediante el operador rango ..:

		

    for $letra (A .. Z) {

        print "$letra ";

    }



También podemos utilizarlo del modo en que tradicionalmente se escribe en C:

		

    @letra = (A .. Z);

    for($letra=0;$letra<=$#letra;$letra++) {

        print "$letra[$letra] ";

    }



foreach

Dicho bucle nos permite iterar igualmente sobre un array del mismo modo que procede la directiva de control for (de hecho son sinónimos para el intérprete):

		

    foreach (<*.pl>) {

        system("copy $_ ${_}.bkup");

        print "Backup de Script : $_ -> ${_}.bkup\n";

    }



En el ejemplo se ha utilizado un atajo muy frecuente en Perl que consiste en utilizar el operador <*.pl> para recuperar el array correspondiente a todos los nombres de fichero presentes en el directorio de trabajo (*) que tengan la extensión pl para realizar una operación de backup sencilla. Hay que notar igualmente que al no intercalarse ningún nombre de variable entre foreach y los paréntesis, debemos recuperar cada uno de los valores por los que el bucle itera mediante la variable por defecto $_

Control de bucles

Perl proporcional un conjunto de directivas para el control de bucles tales como next:

		

    foreach (<*.pl>) {

        next if ( -M $_ > 7.0 );

        system("copy $_ ${_}.bkup");

        print "Backup de Script : $_ -> ${_}.bkup\n";

    }



Así pues, en el ejemplo, realizamos el backup anterior de todos los ficheros cuya fecha de modificación sea inferior a siete días ... puesto que next nos devuelve al inicio del bloque sin realizar ninguna operación en los casos en los que la condición if sea cierta.

Visibilidad: my - our

Perl por defecto considera como visibles o accesibles globalmente (en cualquier punto de un script) todas las variables empleadas a menos que se le indique lo contrario restringiendo la visibilidad de éstas mediante una directiva my, our o local.

El uso de la directiva my antepuesta al uso o asignación de una variable, le proporciona unas características de visibilidad similar al estilo de visibilidad por bloques en C de modo que:

		

     $i = 54;

     for my $i (0 .. 3) {

          print "$i\t";

     }

     print "$i\n";



proporcionaría la siguiente salida por pantalla:

		

     0     1     2     3     54



Puesto que la visibilidad de la variable $i declarada en el bloque for queda restringida a dicho bloque y desde fuera de él accedemos a la variable $i global declarada e inicializada a 54 en la primera linea.

La directiva our sirve para hacer notar explícitamente que usamos una variable global; de éste modo podríamos haber escrito en lugar del ejemplo anterior:

		

     our $i = 54;

     for my $i (0 .. 3) {

          print "$i\t";

     }

     print "$i\n";



Subrutinas

Las subrutinas son piezas de código de uso frecuente susceptibles de ser llamadas en diversas ocasiones con la posibilidad de parametrizar su ejecución mediante una serie de argumentos. En Perl se pueden definir subrutinas mediante una sintaxis del tipo:

		

     sub nombre_subrutina {

          ....

     }



Cualquier argumento pasado a la subrutina en su llamada, podrá ser accedido por las directivas de dicha subrutina mediante el array canónico @_, dicho array puede ser mencionado explícitamente dentro del cuerpo de la subrutina o obviado pues pasa a ser un argumento implícito de funciones y operadores de arrays. Una subrutina devuelve un valor de retorno igual al de la última expresión evaluada en el transcurso de su ejecución. Podemos utilizar igualmente la directiva return para hacer explícita la acción de finalización y retorno del valor correspondiente.

Un ejemplo de la utilización de subrutinas junto con el uso de bucles anidados con directivas de control basadas en etiquetas podría consistir en una búsqueda recursiva de los ficheros contenidos en un árbol de carpetas que hayan sido modificados hace menos de 5 días:

		

     1   #!/usr/bin/perl

     2   print busqueda_recursiva(".",5.0)." ficheros < 5 dias\n";

     3   @x = busqueda_recursiva(".",5.0);

     4   print join("\n",@x);

     5   print "\n";



     6   sub busqueda_recursiva {

     7      my $path = shift;

     8      my $age = shift;

     9      my @array = ();

    10      DIRECTORIO: foreach my $dir ( <$path/*> ) {

    11         next DIRECTORIO unless ( -d $dir ) ;

    12         FICHERO : foreach my $file (<$dir/*>) {

    13            next FICHERO unless ( -M $file < $age );         

    14            if ( -d $file ) {

    15               push(@array,busqueda_recursiva($file,$age));

    16            } else {

    17               push(@array,$file);

    18            }

    19         }

    20      }

    21      @array;

    22   }



En la segunda linea se produce una primera llamada a la subrutina busqueda_recursiva pasándole como argumentos el directorio sobre el que se realizará la búsqueda (en éste caso el directorio actual ".") además del número de días 5. Cabe destacar que en ésta primera llamada, la rutina se ejecuta en un contexto escalar y es por eso, que @array en la linea 21 dentro del cuerpo de la rutina se evaluará del mismo modo, obteniendo el efecto de recuperar el número de ficheros que satisfacen la condición requerida. En la linea 3 la llamada se produce en un contexto distinto (contexto de lista) y por tanto se devuelve el array con el conjunto de nombres de ficheros.

El cuerpo de la subrutina se inicia con dos llamadas a la función shift con el argumento implícito @_ para recuperar el valor de los dos argumentos pasados (i.e. el directorio inicial de búsqueda y el número de días), a continuación se inicializa un array vacío que contendrá la lista de nombres de ficheros. Se inicia un bucle anidado en el que se utilizan explícitamente unas etiquetas DIRECTORIO: y FICHERO: que se utilizarán como delimitadores de las directivas de control de bucles next. Dentro del bucle, existe una llamada recursiva a la misma función en caso de que se necesite profundizar en un subdirectorio y también una llamada a la función push que va acumulando cada uno de los nombres de ficheros que satisfacen las condiciones deseadas.

Referencias y estructuras de datos

Perl 5 incorporó la posibilidad de gestionar las llamadas referencias con objeto de facilitar la gestión de estructuras de datos complejas tales como hashes anidados o matrices multidimensionales. En ésta sección se expondrán las claves de su uso y su aplicación en el contexto del lenguaje.

Introducción

Las referencias son escalares que del mismo modo que los nombres de variables como arrays o hashes pero a un nivel más interno se refieren a la totalidad de valores de un hash, un array o simplemente un único escalar. Su incorporación a Perl se produjo en la versión 5 y se derivó de la frustración que suponía no poder construir arrays de hashes, hashes de arrays y otras estructuras derivadas. A continuación se expondrá la sintaxis básica de construcción y uso de referencias.

Creación de referencias

Existen dos reglas para crear referencias:

Regla de creación No 1

Situando un backslash -> \ como prefijo de una variable, obtenemos una referencia a dicha variable:

		

     1	#!/usr/bin/perl

     2	%datos = (autor => "William Shakespeare", titulo => "Hamlet");

     3	$texto = " Ber. Who's there ? ...";

     4	$referencia_escalar = $texto;

     5	$referencia_hash = \%hash;

     6	print "$referencia_escalar\n";

     7	print "$referencia_hash\n";



La salida del ejemplo nos proporcionaría:

		

SCALAR(0x8060284)

HASH(0x8060308)



Tal y como se puede apreciar, la referencia es una variable escalar que por el momento parece contener una cadena de texto indicativa del tipo de variable al que se refiere junto con un índice interno (0x806...) que utilizará el intérprete para recuperar el contenido de la variable referenciada en cuanto lo requiramos.

Regla de creación No 2

Perl posibilita también la creación de referencias a arrays o hashes anónimos mediante la siguiente sintaxis:

		

     1	#!/usr/bin/perl

     2	$datos = {autor => "William Shakespeare", titulo => "Hamlet"};

     3	$texto = [" Ber. Who's there ? ...", "Fran. Nay, answer me."];

     4	print "$datos\n";

     5	print "$texto\n";



Así pues, mediante los delimitadores { } creamos un nuevo hash que contiene información acerca de la obra y mediante los corchetes [ ] un nuevo array con las dos primeras lineas del texto. En el lado izquierdo de ambas asignaciones, se situa el nombre de una variable que contendrá la referencia al hash y al array respectivamente. Hay que destacar que tanto el hash como el array son elementos que no tienen un nombre (son anónimos), en cambio, existe en ambos casos una variable que alberga una referencia a sus contenidos a la cual nos podemos referir para recuperarlos posteriormente.

Uso de referencias

Del mismo modo que para crear referencias, podemos utilizar dos reglas distintas para recuperar los contenidos a los que se refieren:

Regla de uso No 1

Supongamos que $refa contiene la referencia a un array. Perl interpretará {$refa} como el nombre de dicho array y a todos los efectos será equivalente trabajar con el array derreferenciado o con el original:

		

     @a = qw(a b c d e f);

     $aref = \@a;

     push(@a,g);

     push(@{$aref},h,i);

     print "@a\n";

     print @{$aref}."\n";



En muchas ocasiones, podemos utilizar referencias anónimas a valores de retorno de funciones como split de un modo similar al expuesto a continuación:

		

     $texto = "Homer Marge Bart Lisa Maggie";

     foreach $i ( 0..$#{[split /\s+/, $texto]} ) {

          print "Elemento No. $i = ${[split /\s+/, $texto]}[$i]\n";

     }



Regla de uso No 2

Dado que el uso de gran cantidad de corchetes para derreferenciar contenidos en estructuras de datos complejas oscurece la sintaxis, Perl proporciona una notación alternativa para la recuperación de elementos de hashes o arrays:

		

     ${$aref}[0]     =>     $aref->[0]

     ${$href}{k}     =>     $href->{k}



Así pues, existe un operador arrow -> que nos permite recuperar los valores contenidos en una referencia.

Agunos ejemplos

Como un primer ejemplo del uso de referencias podríamos considerar la siguiente asignación:

		

     @a = ( [1, 2, 3], [4, 5, 6], [7, 8, 9]);



@a es un array con tres elementos, los tres son referencias a otros tantos arrays anónimos. Para recuperar los valores, necesitaremos en primer lugar utilizar cada una de las tres referencias $a[0], $a[1] o $a[2] aplicándoles el operador flecha con el índice del elemento requerido: por ejemplo $a[1]->[2] que contiene el tercer valor del segundo array (i.e. 6). Llegados a éste punto, hay que hacer notar que existe una regla de uso que nos permite prescindir del operador flecha en caso de que se haga constar entre dos índices. De éste modo:

		

     $a[1]->[2]      equivale a       $a[1][2]



Notándolo de manera mucho más próxima a la de un array multidimensional.

Por último, consideremos un problema concreto como podría ser el tratamiento de un fichero cuyo contenido fuera el siguiente:

		

     Chicago, USA

     Frankfurt, Germany

     Berlin, Germany

     Washington, USA

     Helsinki, Finland

     New York, USA

 


para obtener una salida en la que se listen por orden alfabético los paises que constan en el fichero, además de la lista de ciudades pertenecientes a cada uno con un formato del tipo:

		       

     Finland: Helsinki.

     Germany: Berlin, Frankfurt.

     USA:  Chicago, New York, Washington.



El modo más natural de resolver el problema consistiría en la creación de un hash de arrays mediante el uso de referencias y tal como se expone a continuación:

		       

     1   while (<>) {

     2     chomp;

     3     my ($city, $country) = split /, /;

     4     push @{$table{$country}}, $city;

     5   }

     6

     7   foreach $country (sort keys %table) {

     8     print "$country: ";

     9     my @cities = @{$table{$country}};

     10     print join ',',sort @cities;

     11     print ".\n";

     12   }



Éste último ejemplo ha sido extraído del tutorial de uso de referencias perlreftut y en él abundan las expresiones implícitas de variables canónicas, además del ahorro de paréntesis en las llamadas a funciones como join utilizándolas a modo de operadores. El análisis detallado de cada una de las lineas del último ejemplo y los anteriores puede proporcionarnos bastante comprensión acerca de como trabaja Perl y a la vez nos ayude a mejorar nuestro propio estilo de programación.

Bibliografía

  • Programming Perl, Larry Wall, Tom Christiansen and Jon Orwant, 2000, O'Reilly
  • Mark's very short tutorial about references, Mark-Jason Dominus, 1998, The Perl Journal















































Ultima actualización: Julio de 2010
Página mantenida por el equipo de www.lapuebladecastro.com
Correo electrónico: webmaster@lapuebladecastro.com







Casas de Pueblo, Pisos y Apartamentos en el Pirineo

Ya puedes acceder a la nueva Web de Fincas Graus, Expertos inmobiliarios a tu servicio con oficina en Graus y una gran oferta de inmuebles en el Pirineo


Desguaces, solicitud de recambios y
piezas de coche

Ya puedes acceder a la nueva Web de Desguaces Balaguer, tu desguace de confianza, ahora en internet !


Embutidos El Cortante

El que de buen paladar presume, Embutidos El Cortante consume