WikiCiencia - Informática y Computación - Programación - Curso de Perl

Firenox
PRINCIPALELECTRONICAINFORMATICATECNOLOGIANOTICIASDESTACADOS
 

Curso de Perl


Si le resulta útil el contenido de este portal, por favor considere hacer una donación a Cáritas Argentina. Muchas gracias.

1- Introducción

1.1- PERL

1.1.1- Que es PERL

Perl (Practical Extraction and Report Languaje) es un lenguaje de programación surgido a inicios de los noventas, que busca antes que nada el facilitar la elaboración de tareas comunes en sistemas tipo UNIX, donde tradicionalmente las tareas de administración y proceso de datos se realiza con herramientas muy rudimentarias y por demás hostiles al usuario o administrador. Pero que se aplican sobre grandes cantidades de información (por lo regular texto) por lo que se requiere que sean de alto rendimiento.

Su autor, Larry Wall (lwall@netlabs.com) realizó Perl casi como una obra altruista, de modo que hasta PERL 5.X su distribución es gratuita, sin que por eso tenga menos poder o consistencia.

1.1.2- Para que sirve

Perl surgió como una opción para una gran cantidad de herramientas de UNIX en las cuales basa su propia sintaxis, buscando el mínimo sacrificio de su desempeño por una máxima facilidad de programación e integración, sigue la filosofía de mantener un ambiente que sea capaz de detectar y corregir pequeñas omisiones del programador, y de proporcionarle una forma abreviada de realizar múltiples tareas. En una palabra, es una utilería que pretende facilitar el proceso de grandes volúmenes de información sin sacrificar el rendimiento.

1.1.3- Donde Puede Usarse

Las plataformas donde Perl se ha desarrollado mas son los servidores UNIX, por sus necesidades de administración y lo robusto de su manejo de memoria y de procesos (requisitos de PERL hacia el S.O.) además de la facilidad de Perl para realizar los así llamados CGIs, interfaces para comunicar recursos del servidor con un servicio de internet particular (como podría ser WWW o gopher), En otras plataformas, PC en particular, se han desarrollado versiones que mantienen un razonable grado de funcionalidad, pero en realidad, el sistema DOS no tiene un manejo lo bastante bueno de los procesos o de la memoria para permitir a PERL dar un buen desempeño, además de que no es común ver en PC necesidades de administración de la magnitud de un servidor institucional. Sin embargo, puede practicarse la programación en PERL de PC, o incluso elaborar programas de reporteo en el , sin embargo, es algo que no se ha popularizado hasta hoy.

1.1.4- Que fuentes de información existen

Los libros que son ya clásicos sobre Perl son los libros del camello y la llama de la editorial Nutshell, además, existen magníficas introducciones y manuales de referencia que se pueden obtener vía internet. Aun cuando es imposible mencionar con precisión las fuentes de información de un medio tan dinámico con algo tan estático como este documento. Debe notarse, además que estas referencias están en inglés.

Para buscar información, Yahoo! por supuesto:

http://www.yahoo.com/Computers_and_Internet/Languajes/Perl/

Fuente de información general y referencia de documentos:

http://pubweb.nexor.co.uk/public/perl/perl.html

http://www.khoros.unm.edu:80/staff/neilb/perl/perl5.html

Una buena formada introducción:

http://www.khoros.unm.edu:80/staff/neilb/perl/introduction/home.html

Documentación:

http://www-cgi.cs.cmu.edu/cgi-bin/perl-man

http://www.perl.com/perl/info/documentation.html

ftp://ftp.cdrom.com/pub/perl/CPAN/doc/manual/html/index.html

ftp://ftp.uoknor.edu/mirrors/CPAN/doc/manual/html/index.html

Debo recalcar que por la misma naturaleza de Perl, los recursos disponibles y las herramientas que se pueden utilizar cambian muy a menudo, por lo que es indispensable dedicar algún esfuerzo a mantenerse al día para evitar un desperdicio mayor de esfuerzo por no utilizar los nuevos recursos disponibles.

1.2- Filosofía de Perl

"Hay mas de una forma de hacerlo"

-Larry Wall, autor del lenguaje de programación Perl.

Perl no establece ninguna filosofía de programación (de hecho, no se puede decir que sea orientado a objetos, modular o estructurado aun cuando soporta directamente todos estos paradigmas), los objetivos que se tuvieron en cuenta al diseñar la sintaxis de Perl fueron la facilidad de aprendizaje y de uso y la claridad de código, las cuales, considero que son necesarias (aunque pueden escribirse programas en Perl complejos e inteligibles si así se desea).

Por si fuese poco, Perl no es ni un compilador ni un interprete, esta en un punto intermedio, cuando mandamos a ejecutar un programa en Perl, se compila el código fuente a un código intermedio en memoria, se le optimiza (como si fuésemos a elaborar un programa ejecutable) pero es ejecutado por un motor, como si se tratase de un interprete. El resultado final, es que utilizamos algo que se comporta como un interprete pero que tiene un rendimiento comparativo al de programas compilados. Sin embargo, ya existen compiladores de Perl con la versión 5.

En fin, Perl no nos forza a nada, pero como es lógico hay ciertas reglas que recomiendo seguir para facilitar nuestro trabajo:

  • Claridad. En la mecánica de programación actual, los programas deben de ser entendibles por la persona que nos suceda en tareas de mantenimiento, de lo contrario perjudicamos tanto a nuestros compañeros de trabajo como a nuestra propia libertad para progresar y mantenernos libres de preocupaciones.

  • Indentación. Una costumbre ya clásica de la programación, en lo personal, y a lo largo de los ejemplos de este documento, indento el código dos espacios hacia adelante al abrir cada bloque, y termino la indentación al terminar el bloque, de modo que las llaves de apertura y cierre quedan a la vista y en la misma columna, solas en sus renglones (esto incrementa algo el numero de líneas, pero facilita sobremanera la búsqueda y corrección de los diversos bloques de control).

  • Nombres de variables y demás. En lo personal, procuro dar la máxima claridad a los nombres de las variables sin hacerlos demasiado grandes, el nombre de los contadores y variables que guardan valores concernientes a un pequeño segmento de código por lo regular son de un par de letras (c1, c2, ... cx para los contadores, s1, s2, etc para cadenas de entrada etc.) mientras que las variables que afectan a diversos segmentos (a modo de regla, que tienen su definición en una pantalla distinta a donde se usan) tienen nombres explicativos que procuro no excedan los 12 caracteres. Además, los nombres de archivos se usan con mayúsculas (ARCHENT, ARCHSAL, etc) y las clases tienen su primera letra mayúscula.

  • Comentarios. Para facilitar la comprensión de un programa no hay como explicarlo, y los comentarios son el medio ideal para hacerlo, hay por lo menos tres comentarios que considero que siempre deben incluirse en un programa: Que hace el programa, Quien lo escribió y Cuando inicio y termino de escribirlo, sobretodo en el contexto de una organización, estos tres simples comentarios pueden hacer la diferencia entre desechar un programa como indescifrable o dedicarle algún tiempo para revisarlo. Además, considero prudente comentar dentro del código la forma en que el programa deberá ejecutarse, parámetros, y su sintaxis, así como comentar las estructuras de control como un modo de explicar la funcionalidad al detalle y recalcar con comentarios las funciones que cumplen las variables.

  • Sencillez. Es cómodo en ocasiones el comprimir una serie de instrucciones en una sola línea, queda al criterio decidir cuando se gana en claridad con un código mas o menos extenso, pero no debe titubearse en comentar el código que sea "comprimido".

1.3- Diferencias entre Perl 4.3 y 5.X, Como elegir versión

Actualmente existen dos versiones altamente populares de Perl, la 4.3 y la 5.0X, de hecho hay diferencias importantes entre una versión y otra. Seguramente el lector se pregunta porque surge la duda entre usar una versión vieja y una nueva, por regla general las nuevas versiones son mejores que las anteriores de modo que las opacan en todo sentido, Perl no es la excepción a esta regla, el único factor que impide una transición inmediata es que no son 100% compatibles. La versión 5 de Perl es una reescritura total que ya incluye un manejo de estructuras abstractas de datos mucho mas poderoso, incluso, soporta la orientación a objetos a su manera (tema que no trato en esta introducción). De modo que las librerías, por ejemplo para creación de CGIs no funcionan de una función a otra por lo que la migración es poco practica.

Así pues, la decisión sobre que versión utilizar depende del trabajo que haya sido realizado con anterioridad, si ya se tiene un sistema completo o grande en Perl 4, es recomendable mantenerlo en Perl 4 por el resto de su vida útil, pero para desarrollos nuevos es por mucho, mas recomendable iniciarlos con Perl 5 o en su caso, la versión mas reciente que este disponible, por experiencia se que la capacitación para adaptarse a la nueva versión es extraordinariamente corta dada la importancia de las mejoras. Una tercera opción (que es por mucho la mas recomendable si se tienen los recursos) consiste en instalar las dos versiones de modo que convivan en el sistema.

 

2- Programación básica

En este capitulo daremos una rápida revisión a los conceptos mas usuales que se encuentran en un programa en Perl, trataremos de ver la implementación para ambas versiones 4 y 5 cuando sea posible, especificando siempre en que versión esta el ejemplo original (dando preferencia a la versión 5) y al menos las alternativas para implementarlo en la otra versión.

Los ejemplo requerirán para funcionar, que se tenga Perl instalado en la maquina en que se practique y deberá conocerse la ruta completa al binario de Perl.

Mas que sentirse en libertad de experimentar con los diversos ejemplos, deben sentirse obligados a experimentar, modificar y explorar por su cuenta cada que sea posible, tomando los ejemplo solo como un punto seguro de partida. Para este fin, recomiendo que se tenga acceso desde el inicio a alguna referencia del lenguaje (ya sea el libro Programming Perl (del Camello) de la Nutshell o alguna referencia disponible por WWW), este documento pretende explicar lo básico de un modo accesible para que después pueda el nuevo programador de Perl abordar sin temor los materiales mas detallados.

2.1- Estructura Básica de un programa, programa Holamundo.

Como se menciona en la introducción, Perl no obliga a casi nada, así pues, lo que planteo como estructura básica es mas bien una convención que un requisito del lenguaje, a diferencia de Pascal (por ejemplo) Perl no tiene una plantilla para sus programas y si se adoptan algunos protocolos es solo por comodidad.

Los programas de Perl, por lo regular, inician con la línea:

#!/usr/bin/perl

Esta línea, indica al SO que lo que sigue es un script de Perl, y que "Perl" (el programa con el cual debe ejecutarse) esta en el directorio "/usr/bin", la secuencia "#!" es una secuencia que UNIX reconoce, no Perl.

Un método alterno, que funciona para otras plataformas, es: en lugar de colocar esa primera línea ejecutamos:

Perl nombre_del_script.pl 

de modo que directamente se ejecuta el interprete de Perl pasándole como primer parámetro el script a ejecutar (los demás parámetros se tomaran como parámetros al programa). Si se requiere deberá sustituirse "Perl" por la ruta completa del programa y el nombre que el programa tenga.

Para los ejemplos sucesivos, tomare la suposición de que se trabaja en un sistema UNIX con el interprete de Perl en el directorio "/usr/bin".

Y eso es toda la estructura básica!.

Programa Hola mundo:

#!/usr/bin/perl
print "Hola Mundo\n"; #Saluda
#Programa Hola Mundo, Daniel Sol Llaven, 1996, como parte del tutorial de Perl

Este programa, se escribe como un archivo de texto común, (al que recomiendo se le ponga extensión ".pl") y se cambian sus permisos para que pueda ser ejecutado (por lo regular con un "chmod 700 nombre_programa.pl" en sistemas UNIX), para ejecutarlo simplemente se invoca el nuevo script "nombre_programa.pl", hay que recordar que para el sistema particular en que se este trabajando es muy probable que deba modificarse "/usr/bin/" por otra ruta.

Así, la ejecución en un sistema UNIX podría verse como:

>chmod 700 Hola.pl
>Hola.pl
Hola Mundo
>Hola.pl
Hola Mundo
>

Ejecutando dos veces el script pretendo mostrar que no es necesario cambiar el modo de ejecución del script sino solo una vez.

Expliquemos las tres líneas que constituyen nuestro primer script:

#!/usr/bin/perl

Esta línea, propiamente dicho, no es parte del script de Perl, sino una instrucción al SO para indicar que las siguientes líneas deberán interpretarse por el programa "/usr/bin/Perl", si se omite, o no funciona en el sistema operativo, podemos ejecutar el programa de la siguiente forma:

>/usr/bin/Perl Hola.pl
Hola Mundo
>print "Hola Mundo\n"; #Saluda

Esta es la única instrucción del programa y esta acompañada por un comentario, la instrucción es:

print "Hola Mundo\n";

que pudo ser escrita como:

print("Hola Mundo\n");

sin que esto implicara el mínimo cambio en funcionalidad.

a continuación tiene el comentario:

#Saluda

La sintaxis de C para comentarios no funciona en Perl, pues se usa a "/" para expresiones regulares.

#Programa Hola Mundo, Daniel Sol Llaven, 1996, como parte del tutorial de Perl

Esta línea es otro comentario, el SO y Perl consideran todo lo que este en una línea que inicie con "#" como comentario. (excepto en el caso de que sea una primera línea, el SO lo interpreta como la indicación del interprete, pero Perl lo ignora como a cualquier otro comentario).

2.2- Estructuras de Datos Básicas

En nuestro programa Hola.pl no utilizamos ninguna variable, por lo que para explicar estos conceptos necesitaremos un nuevo programa, HolaP.pl que es una versión personalizada de Hola.pl.

#!/usr/bin/perl
#Programa Hola Mundo personalizado, Daniel Sol Llaven 1996, Tutorial Perl.
print "Hola ?como te llamas?:";
$nombre=<STDIN>;
chop($nombre);
print "$nombre!, bonito nombre, cuantos años tienes?:";
$edad=<STDIN>;
print "sabias que te faltan ".(100-$edad)." para tener 100?\nAdios!\n";

Su ejecución genera resultados similares a los siguientes:

>HolaP.pl
Hola ?como te llamas?:Daniel
Daniel!, bonito nombre, cuantos años tienes?:22
sabias que te faltan 78 para tener 100?
Adiós!
>

En este programa, aparecen muchos elementos novedosos que iremos revisando poco a poco a lo largo de este documento, por el momento, revisemos que hace línea por línea:

#!/usr/bin/perl
#Programa Hola Mundo personalizado, Daniel Sol Llaven 1996, Tutorial Perl.

Cabecera normal del programa, incluido comentario indispensable.

print "Hola ?como te llamas?:";

Esta línea escribe "Hola ?como te llamas?:" pero nótese que no escribe una vuelta de carro al final (omite el "\n").

$nombre=<STDIN>;

Aquí asignamos a $nombre un renglón escrito por la entrada estándar del programa.

Perl define al "archivo" STDIN como su entrada estándar, y el operador <> que indica la lectura de una línea de un archivo de texto, de modo que <STDIN> indica la lectura de un renglón de la entrada estándar (al menos para el ejemplo!), este tema se tratara con cuidado en la sección 2.5 y 3.3 de este documento que tratan de operaciones con archivos.

Para fines prácticos, usaremos <STDIN> como la expresión que lee una línea de entrada del usuario desde teclado.

chop($nombre);

Perl, al leer la línea escrita por el usuario, también toma el enter que el usuario da, de modo que en el ejemplo, la entrada que di en vez de ser "Daniel" se puede escribir como "Daniel\n", (siendo "\n" el carácter de vuelta de carro), la función "chop()" tiene la función de eliminar el ultimo carácter a nuestra cadena, de modo que después de esta instrucción $nombre solo tiene "Daniel". Por las características de la lectura de renglones de archivos, chop() es un elemento casi constante en programas de Perl.

(Pruébalo!, revisa que sucede si comentas esta línea, veras que al imprimir el nombre imprime también el retorno de carro avanzando una línea hacia abajo).

print "$nombre!, bonito nombre, cuantos años tienes?:";

Esta línea nos muestra como se imprime el contenido de nuestras variables de forma sencilla; notamos que en la cadena a imprimir se encuentra $nombre, el compilador lo interpreta y reemplaza por su valor, (para imprimir "$" se pone "\$"), el resto de la cadena es completamente convencional.

$edad=<STDIN>;

Esta línea, lee otra línea de TEXTO y la coloca en la variable $edad (con todo y retorno de carro).

print "sabias que te faltan ".(100-$edad)." para tener 100?\nAdios!\n";

Esta línea imprime un mensaje que esta dividido en tres partes, la primera y la ultima son cadenas convencionales, que están concatenadas (operador .) con una operación aritmética. Hay que notar que edad era un renglón de la entrada estándar, y no un numero, sin embargo no hay conflicto de tipo al operarlo con un entero, ¿porque? porque los dos, para Perl, son del mismo tipo, son escalares, después discutiremos que son los escalares y que mecanismo de conversión se utiliza para extraer el entero del texto contenido en $edad.

(Experimenta!, prueba que pasa si los valores que se dan a edad no son enteros, que pasa si son reales o cadenas!)

Después de nuestro primer encuentro con las variables de Perl, el sentimiento mas probable es el de perplejidad, ¿como maneja sus variables?, ¿a que me refería conque enteros, reales y cadenas son del mismo tipo?, y si es así, ¿como serán los demás tipos?.

En realidad, las variables de Perl pretenden simplificarnos la vida, pero debemos comprender como pretenden facilitar las cosas para no terminar peleando contra ellas.

2.2.1- Clases de Datos

Perl reconoce tres clasificaciones básicas de datos, y dos especiales, por claridad, llamaremos clases a estas diversas clasificaciones, y tipos a las formas usuales de datos.

Las diversas clases se distinguen entre si por el símbolo que antecede al nombre de la variable (por ejemplo $nombre es una variable de tipo escalar que se llama "nombre"), debe notarse que no hay relación entre variables del mismo nombre si son de clases distintas.

A continuación, pongo una tabla de las clases de datos y los tipos que contienen:

Clase         Símbolo         Tipos                                        

Escalar       $               Entero, Real, Cadena, Referencia*            

Arreglo       @               Arreglo de escalares                         

Hash          %               Arreglo Asociativo de escalares              

Archivo       (ninguno)       Identificador de Archivo                     

Type Glob     *               Cualquiera**                                 


* Las referencias son exclusivas de Perl 5, son el equivalente a apuntadores.

** Las variables tipo Glob se usaban como sustituto de las referencias en Perl 4, son obsoletas para Perl 5 y en lo personal no recomiendo su uso sino en casos muy particulares.

2.2.1.1- Escalares

Nota: Algunas características de las conversiones entre tipos son exclusivas de Perl 5, pero la sintaxis y características generales son las mismas para ambas versiones. Por lo que en general, el tratamiento expuesto es valido para las dos.

El escalar, es la clase de datos que engloba los tipos convencionales de datos, de modo que podemos decir:

$v1="Daniel Sol Llaven";
$v1=100;
$v1=89.12;

Sin que esto implique ningún cambio en la naturaleza de $v1, en todo momento es un escalar.

Aun cuando la compatibilidad de datos enteros y reales es fácil de imaginar, la conversión implícita en el caso de las cadenas no lo es, sin embargo la regla es bastante simple.

por ejemplo:

$v1="123y321";

crea un escalar $v1, que contiene la cadena "123y312", ¿pero que pasa si le deseamos sumar 4?. Perl, interpreta $v1 como entero (o punto flotante) para esto, toma todos los caracteres del inicio que forman un numero correcto y ese numero es el que interpreta; de modo que:

print $v1+1;

imprimirá:

124

Del mismo modo, como ejemplo:

$v2="123.123.123"+1

da el valor 124.123 a la variable $v1 (punto flotante).

Otro punto importante de las variables en general es que, aunque en ningún momento se declaran como de un tipo o de otro, si se pueden "destruir", o revisar que hayan recibido algún valor en la ejecución del programa, esto se logra mediante las funciones defined() y undef(). defined nos indica si la variable que le pasamos como argumento ha sido definida (es decir, existe en el programa) o no. undef toma una variable y la "elimina" de modo que ya no tiene valor y defined la reporta como no utilizada.

Los valores lógicos de verdadero y falso se manejan de modo similar al de C, cualquier valor escalar no nulo o cero se considera verdadero. Debe tenerse cuidado con las cadenas "0" pues si se evalúan como número resultaran en falso aun cuando no son cadenas nulas.

Los escalares son los constituyentes de las demás estructuras de datos, por lo que al explicar los arreglos, hashes, referencias y archivos haremos muchas referencias a ellos.

2.2.1.2- Arreglos

Los arreglos en Perl son simplemente, arreglos dinámicos de escalares, es decir, se pueden usar cualesquiera elementos en el arreglo y Perl se encargará de hacer al arreglo de la dimensión adecuada.

La definición de un arreglo con valores constantes es:

@a1=("hola",123,43.2,"adios");

Esta definición, crea el arreglo @a1 con cuatro elementos, dos cadenas, un entero y un real, en realidad, cuatro escalares, para hacer referencia a los elementos escalares de un arreglo se usan los corchetes [] indicando el índice del elemento (de cero al numero de elementos menos uno) de modo que:

print "$a1[0]\n$a1[1]\n$a1[2]\n$a3[3]\n";

resulta para el arreglo anterior:

hola
123
43.2
adiós

Nótese que el elemento de un arreglo ya no es un arreglo, sino un escalar, y debe especificarse como tal, con $ en vez de @. No es lo mismo el escalar a1 que un elemento del arreglo a1.

$a1 Es un elemento distinto que $a1[]

Por claridad, recomiendo que no se dupliquen nombres de arreglos (o cualquier otra cosa) con escalares, aunque Perl da un manejo independiente a las diversas entidades, de modo que si se hace no habrá quejas de parte de Perl.

Es importante señalar que los arreglos y hashes no pueden ser elementos de un arreglo, si se intenta esto, los arreglos o hashes serán "aplanados" a elementos que se agregan al arreglo. Por ejemplo:

(a,b,(c,d),e)==(a,b,c,d,e)

Es decir, el arreglo constante a,b, arregló con c,d , e es equivalente al arreglo que contiene a,b,c,d,e. Pues al formar el primer arreglo, el sub-arreglo c,d es "aplanado" a elementos que se agregan al arreglo principal. Algo similar sucede a los hashes.

Para hacer arreglos de arreglos o arreglos como elementos de hashes, se utilizan las referencias.

2.2.1.3- Hash o Arreglos Asociativos

El Hash, o arreglo asociativo es una ampliación del arreglo, en la que en vez de ubicar los elementos por posición se les ubica por una "llave" es pues un arreglo de parejas ordenadas que se accesa por el primer elemento, el símbolo de los hashes es %, y la forma de declarar un hash con constantes es muy similar a la forma para declarar un arreglo:

%h1=("ll1",1,"ll2",2,"ll3",3);

Esta declaración crea un hash con tres parejas ordenadas, las llaves son "ll1", "ll2", "ll3", para usarlo se pueden usar expresiones del tipo:

$ndx="ll3";
print "$h1{ll1}\n$h1{"ll2"}\n$h1{$ndx}\n";

resultando:

1
2
3

Al igual que en los arreglos, cada elemento de un hash es un escalar, de modo que debe anteponerse $ en vez de % (pues no estamos haciendo referencia a todo el hash, sino a un elemento escalar del hash), pero a diferencia del los arreglos, en vez de usar [] para indicar el índice se usan las llaves {}.

Dentro de las llaves, en el ejemplo, usamos las tres formas principales de dar el índice:

ll1 - En esta forma Perl adivina correctamente que ll1 es una cadena y que esta es la llave.

"ll2" - En esta forma decimos explícitamente el valor de la llave deseada.

$ndx- como $ndx="ll3" Perl puede determinar el valor de la llave correcta.

Para índices, al igual que para hashes, también puede usarse el valor de variables o de expresiones para especificar el índice.

2.2.1.4- Equivalencias de Clases

Como ya revisamos, al hacer referencia a un elemento particular de un arreglo o hash obtenemos un escalar (en ves de todo el arreglo o hash). Este tipo de "cambios" de clase son el propósito de esta sección, pues pueden resultar algo confusos aunque, una vez comprendidos, dan una buena parte del sabor peculiar de Perl.

Básicamente, Perl distingue dos tipos de contextos para evaluar una expresión: como escalar y como arreglo. El primero se refiere a las expresiones que han de regresar un escalar (del tipo que sea) como resultado, y el segundo a aquellas expresiones que han de regresar un arreglo o conjunto de escalares como resultado. Muchas expresiones pueden ser evaluadas en ambos contextos y obtener, según el contexto, un resultado distinto, esto lo revisaremos con cuidado conforme vayamos revisando estas expresiones.

Revisemos los cambios que ocurren a los datos de una cierta clase cuando los evaluamos en otro contexto:

Contexto            escalar         arreglo                                  

           Clase    escalar         arreglo           hash                   

escalar    escalar  El valor        Se convierte en   Vacío, no definido     
                    original        el único                                 
                                    elemento del                             
                                    arreglo                                  

arreglo    arreglo  Numero de       El arreglo de     Los elementos pares    
                    elementos       valores           (0,2,4,...) forman     
                    (usualmente)    originales        las llaves y los       
                                                      nones los datos del    
                                                      nuevo hash.            

           hash     cociente que    arreglo con las   El hash original       
                    representa la   parejas                                  
                    eficiencia del  llave1,valor1,lla                        
                    hash            ve2,valor2,...                           


Las transiciones mas utilizadas son las de arreglo a escalar, las de arreglo a hash y de hash a arreglo, la primera porque permite conocer el tamaño de los arreglos y las segundas porque proveen los mecanismos para inicializar los hashes con arreglos constantes y para "aplanarlos" con fines de almacenamiento e impresión.

A menudo, se representa un arreglo con una sola cadena que contiene separadores para los diversos elementos, Perl implementa la función "split" que divide estas cadenas en arreglos, su sintaxis básica es:

split($delim,$cadena)

donde

$delim es la expresión que representa los delimitadores de elementos y

$cadena es la cadena a dividir.

Como ya se mencionó, generará un arreglo de cadenas donde los elementos son las subcadenas de $cadena que estén separadas por cadenas $delim.

por ejemplo, una cadena como:

$registro="Daniel:22:daniel@simba";
@reg=split(":",$registro);
print "Nombre:$reg[0]\nEdad:$reg[1]\nEmail:$reg[2]\n";

genera un resultado similar a:

Nombre:Daniel
Edad:22
Email:daniel@simba

Cuidado! no todas las funciones de Perl convierten de igual forma los arreglos en escalares, por lo que debe provarse o investigar primero que efectivamente en el contexto en que hagamos la conversión el resultado sea el deseado.

Ejemplos:

Arreglo constante

@arreglo=(1,2,"hola",3,"adios");

inicializa el arreglo @arreglo con los elementos 1,2,"hola",3,"adios", (todos escalares). la notación de los elementos entre paréntesis define un arreglo constante, el equivalente a un numero o cadena constante cuyo valor asignáramos a una variable pero en el contexto de los arreglos.

"Hash" constante

%hash=("llave1","dato1","llave2","dato2);  #arreglo constante a hash

Inicializa el %hash con las llaves "llave1" y "llave2" poniéndole como contenidos "dato1" y "dato2" respectivamente. De hecho, no especificamos un "hash constante" como en el caso del arreglo, sino que especificamos un arreglo constante el cual pasa por una transformación de clase para asignarse a un hash, de modo que la expresión es equivalente a:

@arreglo=("llave1","dato1","llave2","dato2); #arreglo constante a arreglo
%hash=@arreglo;	#arreglo a hash

Cardinalidad de un arreglo

@arreglo=(1,2,3,4);
$elementos=@arreglo;

En este caso $elemento recibe el valor 4, pues son 4 los elementos del arreglo, nótese que el índice máximo en el arreglo es de solo tres, pues el primer elemento es el elemento 0 y Perl 4 regresa, con esta misma expresión el índice máximo en lugar del número de elementos como Perl 5.

Asignación a arreglo constante

($a,$b,$c)=(1,2,3);

Esta expresión es equivalente a:

$a=1;
$b=2;
$c=3;

Pero debe tenerse cuidado, el siguiente código:

($a,$b,$c)=(1,2,3);
@a=($a,$b,$c);
$a=7;
$b=8;
$c=9;

Da al arreglo @a el valor del arreglo constante (1,2,3), y no, como podría pensarse. (7,8,9). Cuando se genera un arreglo constante se evalúan los elementos que lo forman como constantes escalares y se le asigna después, para obtener resultados diferentes se deberá usar un arreglo de referencias.

Arreglos con arreglos

@a=(1,2,3);
@b=(5,6,7);
@c=(@a,4,@b,8);

Estas expresiones generan tres arreglos, (1,2,3), (5,6,7) y (1,2,3,4,5,6,7,8), y no, como podría pensarse un arreglo de arreglos, cuando incluimos un arreglo dentro de otro, Perl "aplana" el arreglo insertado como si insertara uno a uno todos sus elementos en la posición indicada del arreglo que ha de contenerlos, para hacer arreglos de arreglos deberemos usar referencias a estos.

2.2.3- Tipos Especiales de Datos

Llamo a estos "tipos especiales" porque nos permiten hacer cosas inposibles con otros tipos, por ejemplo, el evitar ser "aplanado" en los arreglos. Además, incluyo en esta sección a los Archivos, pues aunque su sintaxis se parece a las de las variables, su funcionalidad es bien distinta. Sin embargo, por tratarse de solo una introducción omito discutir sobre los "type globs" que son la aproximación a las referencias que perl 4 implementa.

2.2.3.1- Referencias

Nota: Este tipo de datos es exclusivo de Perl 5, Perl 4 usaba los type globs para realizar algunas de estas funciones, pero el proceso es mucho mas complicado y obscuro.

Las referencias son el equivalente lógico de los apuntadores, son un tipo de dato que no contiene información en si misma, sino que permite manejar indirectamente los datos contenidos en otra entidad.

Las referencias, sin embargo, no forman una clase nueva de datos, sino que son tratados como un tipo mas de escalar.

La definición de referencias se realiza por el operador referencia "\" (backslash) , funciona de modo similar a "&" en C, y aunque a diferencia de C no hay un operador de derreferencia la sintaxis para acceder al dato original es muy sencilla.

A diferencia de C, los objetos que creemos mediante referencias no quedan como cadáveres en memoria, Perl lleva un registro de las diversas referencias a cada elemento en memoria y automáticamente lo destruye cuando descubre que nadie hace ya referencia a él, de modo que pueden usarse las referencias sin miedo, aunque con la precaución necesaria para no perder los datos que almacenamos en ellas.

2.2.3.1.1- Creación

Podemos crear referencias de varias formas, las que considero mas sencillas y útiles son:

Usando el operador de referenciación en una variable, o valor, en el caso de una variable, es crear un medio alterno de acceder al valor de la variable.

$rescalar=\$escalar;
$rarreglo=\@arreglo;
$rhash=\%hash;
$rrutina=\&subrutina; #la programación de subrutinas la revisaremos mas adelante.
$globref=\*ARCHIVO; #solo revisaremos los type globs para usarlos como referencias a archivos.

Creando objetos "anónimos" que solo pueden ser accesados por medio de la referencia.

Escalares

$rescalar=\"hola";	#referencia a la cadena anónima "hola"

Arreglos:

$rarreglo=[1,2,3];	# referencia al arreglo anónimo (1,2,3)

Hashes

$rhash={"llave1" => "dato1","llave2" => "dato2"};

Nótese que en esta representación usamos además el operador "=>" que indica una pareja de llave y dato, las que se separan por comas; esta notación también es valida para hashes convencionales, pero la claridad que agrega es mejor utilizada al declarar hashes anónimos.

Como elementos de arreglos y/o hashes para formar estructuras de datos complejas, al ser escalares, son elementos del arreglo sin mas complicación, de modo que los arreglos a los que hacen referencia se mantienen intáctos.

Por ejemplo, para formar un hash que contenga arreglos de nombres de letras:

%rhletras={
"latinas" => ["a","b","c"],
"gregas" => ["alfa","beta","gamma"]};

Esta sentencia forma un hash donde las llaves son cadenas y los datos son referencia a arreglos.

Pueden convinarse las referencias a arreglos y a hashes anónimos a voluntad para formar una estructura de datos tan compleja como se desee.

2.2.3.1.2- Uso

De nada sirven las referencias si no podemos extraer y modificar los datos contenidos en los elementos señalados por la referencia, en Perl la forma para obtener el valor y para modificarlo es casi la misma, solo las abreviaturas de las referencias para obtener el valor funcionaran de modo distinto cuando tratemos de asignarles valor.

Aun cuando Perl tiene varias formas de "derreferenciar" la información, solo discutiré dos por considerarlas las mas sencillas, sin embargo, recomiendo una revisión al capitulo PERLREF de la referencia de Perl 5.X para una explicación mas detallada y a consciencia del manejo de las referencias.

Uso "simbólico" de las referencias.

Por regla general, podemos usar las referencias como si se sustituyeran antes de ejecutar el código (aunque en realidad, no sea así), de modo que el código:

$nombre="entero";
$entero=5;
$rentero=\$entero;
$$nombre=6;
$$rentero=7;

efectivamente cambia el valor de $entero de 5 a 6 y despues de 6 a 7, del mismo modo podemos usar las referencias refiriéndonos a cualquier otro tipo.

por ejemplo, para arreglos:

$rarreglo=[1,2,3,4]	#crea arreglo anónimo (1,2,3,4)
$$rarreglo[2]="tres";	#modifica el arreglo anónimo a (1,2,"tres",4)
@$rarreglo=();		#limpia el arreglo anónimo

Uso con el operador de derreferencia.

Como una forma de abreviar el proceso para referencias a arreglos y hashes, se añadió el operador -> que indica que el escalar es una referencia a hash o arreglo y espera el índice después.

por ejemplo:

$rarreglo->[5]="algo";

coloca "algo" como el 6o elemento del arreglo al que hace referencia $rarreglo.

$rhash->{"alfa"}="uno";

coloca la pareja ("alfa" => "uno") en el hash al que hace referencia $rhash.

Uso abreviado del operador de derreferencia.

Se pueden obtener referencias a referencias, y se pueden hacer arreglos de referencias, de modo que los arreglos multidimencionales se pueden elaborar con la misma mecánica que en C.

$ra3d->[0]->[0]->[0]=1; #pone un "0" en la primera celda de un arreglo tridimencional

Nótese que al crear perl los arreglos de las dimensiones adecuadas automáticamente no hay problemas de invasión de memoria no reservada (aunque un uso descuidado puede ocupar cantidades demasiado grandes de memoria). Y esta aplicación se considero lo bastante frecuente para implementar una abreviatura, de modo que la expresión anterior es equivalente a:

$ra3d[0][0][0]=1;

Lo cual le da mas claridad de lectura al código (pero debe tenerse cuidado de no confundirlo con un elemento de un arreglo, $ra3d es una referencia a un arreglo de referencias y no un arreglo normal. Por claridad podríamos usar:

$ra3d->[0][0][0]=1;

Ejemplo: A continuación, coloco el listado de un programa que usa las estructuras de datos que mencionamos anteriormente usando su potencia lo mas posible sin que pierdan claridad, recomiendo que no solo se pruebe el programa, sino que se elabore una versión en la que se exploren los puntos que particularmente les resulten mas interesantes.

2.2.3.2- Archivos

Uno de los usos mas probados y comunes de Perl es el procesamiento de archivos de texto para generar otros archivos (por lo regular de texto) que implican cierto proceso de los datos leídos originalmente. Además, Perl no conoce mas entrada que la proveniente de archivos (asociando la entrada, salida y salida de errores a archivos "de ambiente") por lo que en cierta medida ya hemos revisado el uso básico de archivos, solo que ahora le daremos la explicación correspondiente a las entidades que vimos antes.

Como en cualquier otro lenguaje, el ciclo de uso normal de un archivo es:

Apertura       Mediante "open" inicializa una variable de archivo         

Uso            Lectura secuencial por líneas                              

Cerrado        Mediante close                                             


Perl tiene implementaciónes para manejar archivos binarios y archivos aleatorios, pero el uso de archivos de texto secuenciales es lo mas común en UNIX, por lo que considero a este manejo como básico y le explico aquí.

Básicamente, se puede decir que hay tres tipos de archivos que se manejan de modo muy similar:

  • Archivos comunes

  • Programas de los que capturamos la entrada o la salida

  • La entrada, salida o salida estándar de errores.

Estos tres tipos de archivos se manejan igual (con la limitación de que algunos solo reciben lectura o escritura) y el momento en que se determina el tipo verdadero del archivo, es en el momento de la apertura, por lo que ha dedicado una sección exclusivamente a la apertura mientras que al uso y cierre los he concentrado en la sección que siguiente a esta.

2.2.3.2.1- Apertura

La entrada, salida y salida de errores estándar, están abiertas por default, pero en cualquier otro sentido se utilizan igual que cualquier otro archivo, debe tenerse cuidado al planear los entubamientos de información para evitar que nuestro programa espere por siempre una entrada que no le ha de llegar, sin embargo no es necesario ser paranoico al respecto. Si tratamos de usarlas después de cerrarlas el resultado de las lecturas será nulo y los intentos de salida no tendrán efecto.

Los archivos convencionales se abren con la instrucción "open", la cual es, por decir poco, muy interesante, en esta sección solo revisaremos su uso básico.

open VARCHIVO,EXPRESION

VARCHIVO es la variable mediante la que usaremos el archivo, no tiene indicador de clase y por convención la manejamos en mayúsculas.

EXPRESIÓN es, digamos, el nombre y modo en que habrá de abrirse el archivo.

Los archivos, se darán con los nombres nativos del S.O., y los modos se especifican con las siguientes cadenas al inicio del nombre (que imitan al shell).

<archivo    abre "archivo" para lectura                                   

>archivo    abre "archivo" para escritura, borrándolo si existe.          

>>archivo   abre "archivo" para escritura, agregando la nueva             
            información al final                                          

+<archivo   abre "archivo" para lectura y escritura                       

+>archivo   abre "archivo" para lectura y escritura borrando "archivo" al abrirlo.   

|programa   ejecuta "programa" y reasigna su entrada estándar a lo que    
            escribamos en el                                              

programa|   ejecuta "programa" y reasigna su salida estándar para         
            lectura de nuestro programa                                   


Debe notarse que a diferencia de los archivos no se puede ejecutar programas para lectura Y escritura.

Es importante notar que esta forma de ejecutar programas desde perl es muy poderosa, pero no es la única, por lo regular se usa "system" para ejecutar programas si la salida no nos importa o encerrando con las comillas ` al comando si la salida es sencilla (un renglón) lo que evalúa al comando como si fuese una expresión resultando en la salida del programa (las comillas ' funcionan de distinta forma entre Perl 4 y 5 al ejecutar el programa).

También se pueden abrir la salida y entrada estándar mediante open, pero estas funciones así como la de cerrarlas puede tener consecuencias en la ejecución del programa que son algo complicadas de explicar, por lo que recomiendo que de desear manejar estas operaciones se use la referencia de Perl.

2.2.3.2.2- Uso y Cerrado

Básicamente, se puede usar un archivo de dos formas, por renglones, (terminados por el carácter de vuelta de carro) o por carácter (byte). Revisare solo la primera opción, porque el manejo por carácter tiene implicaciones sobre la naturaleza del sistema de explicación muy larga y porque son mas usuales los archivos de texto en las aplicaciones de Perl.

El operador que nos permite leer de un archivo es "<>" teniendo la variable de archivo dentro de el, así por ejemplo:

$reng=<STDIN>;

Que, como ya hemos mencionado; lee un renglón de la entrada estándar y lo coloca en la variable $reng, incluida la vuelta de carro que termine el renglón.

Si el renglón termina con carácter de fin de archivo, este no se incluye en el renglón, y lecturas sucesivas de STDIN esperaran indefinidamente por una nueva entrada (así que debe tenerse cuidado al usar la entrada estándar o confiar en el usuario).

STDIN es la variable de archivo que identifica la entrada estándar, así como

STDOUT es la variable que identifica la salida estándar y

STDERR es la que identifica la salida estándar de errores.

@contenidos=<STDIN>;

Esta vez, estamos evaluando al operador <> en un contexto de arreglo, y su comportamiento varia, como arreglo <> regresa un arreglo de cadenas, donde cada cadena es un renglón del archivo, de modo que la expresión anterior leerá todo lo que se introduzca en la entrada estándar y lo coloca en el arreglo @contenido. Esta es una forma mas segura de usar la entrada estándar, pues ahora cada elemento del arreglo equivale a una lectura sucesiva de STDIN en contexto escalar.

Si sustituimos STDIN por cualquier otra variable de archivo (o valor de variable de referencia a una variable de archivo) estaremos cargando el archivo ya sea renglón por renglón o todo completo a variables de memoria.

Como caso especial, si evaluamos la expresión:

<ARCHIVO>;

cargaremos un renglón del archivo al que ARCHIVO hace referencia y lo coloca en la variable $_ de la cual hablaremos mas adelante.

A continuación, esta el listado de un programa sencillo realizado en perl que demuestra la potencialidad de las estructuras de datos y lo básico del manejo de archivos, este programa esta hecho para perl 4, pero no creo que tenga problemas para correr bajo perl 5, sin embargo, debe recordarse que la ruta hacia perl muy probablemente deberá cambiarse o deberá ejecutarse perl dándole el programa como parámetro.

#!/usr/bin/perl
# Programa hecho en perl4 que crea un archivo de parejas de nombre y 
# dirección IP tomando las direcciones de un archivo de la forma:
#
#131.178.80.32
#206.103.64.98
#ppp16-07.rns.tamu.edu
#130.178.80.20
#
# Etc, entiendase que los "#" no están en el archivo de entrada que 
# tiene el nombre "salida" y que esta en el directorio activo.
open(DIR,"<salida");		# Abre el archivo de entrada
while($dir=<DIR>)		# Lee todas sus líneas una a una
{
  chop $dir;			# les corta el fin de línea
  open(NS,"nslookup $dir|");	# Invoca nslookup y recibe el resultado
  undef(@inf,$nombre,$inf);	# Destruye datos viejos
  while($reng=<NS>)		# revisa el resultado del nslookup
  {
    chop $reng;			# quita fin de línea
    $reng=~s/ //g;		# quita espacios
    @inf=split(":",$reng);	# Hace un arreglo con los resultados				
# que vienen como "Name:  servidor.unam.mx"
                                # "Address:  132.248.100.100" por ejemplo.
    if($inf[0] eq "Name") {$nombre=$inf[1]};	#Toma el nombre del servidor
    if($inf[0] eq "Address") {$dir=$inf[1]};	#Toma su dirección IP
  }
  close NS;			# Cierra los resultados y termina la ejecución de nslookup
  if(defined($nombre))		# Si asigno valor a $nombre...
  {
    $hres{$dir}=$nombre;	# Llena un Hash de la dirección y el nombre
    print "%\n";		# Indica el éxito de la resolución
  }
  else
  {
    print "#\n";		#Indica el fracaso de la resolución
  }
}
close(DIR);			#Cierra su archivo de entrada
open(DIRS,">direcciones");	# Abre archivo de salida de resultados
print "\nimprimiendo resutlados...\n";
foreach $dir (keys %hres)	# Para todas las llaves de %hres
{
  print DIRS "$dir $hres{$dir}\n";	#imprime la pareja llave-valor
}
close(DIRS);			# cierra su archivo de salida.

2.3- Operaciones Básicas

Como se habrá podido distinguir en los ejemplos vistos hasta ahora, Perl cuenta con una amplia y muy interesante gama de operadores, en esta sección daremos una rápida revisión a los mas importantes, confiando en que el uso de la mayoría de los operadores será casi intuitivo.

Para obtener mayor información sobre los operadores, la sección PERLOP de la referencia es la mejor fuente de información detallada.

Las variables, comillas, paréntesis y funciones con paréntesis tienen la precedencia mas alta en perl, de hecho, Perl los interpreta mas como operadores unarios que se comportan como funciones por la agrupación de los parámetros sobre los que actúan. Después de estos, la precedencia de los operadores mas usuales es:

Mas alta:

Posición       Operador                                                   

no asocia      ++ y --                                                    

derecha        ! ~ \ y la versión unaria de + y -                         

izquierda      =~ !~                                                      

izquierda      * / % x                                                    

izquierda      + - .                                                      

no asocia      < > <= >= lt gt le ge                                      

no asocia      == != <=> eq ne cmp                                        

izquierda      &                                                          

izquierda      | ^                                                        

izquierda      &&                                                         

izquierda      ||                                                         

no asocia      ..                                                         

derecha        ?:                                                         

derecha        = += -= *= etc.                                            

izquierda      , =>                                                       

izquierda      not                                                        

izquierda      and                                                        

izquierda      or xor                                                     


Mas baja.

2.3.1- Operaciones Aritméticas

Estas operaciones interpretaran al escalar como un numero (ya sea entero o real) y operaran aritméticamente sobre el, toda cadena que no inicie con una cifra será interpretada como un cero por estos operadores.

En orden de Precedencia tenemos:

++ y -- Son los operadores de auto incremento y auto decremento tan conocidos para los programadores de C. Su uso se puede resumir en la siguiente tabla:

Posición     ++                             --                             

Antepuesto   Incrementa 1 antes de evaluar  Decrementa 1 Antes de evaluar  

Pospuesto    Incrementa 1 después de        Decrementa 1 después de        
             evaluar                        evaluar                        


Así pues de las expresiones:

$var=5;
print $var++;
print "\n$var\n";

obtenemos impresos los números:

5
6

Debido a que en el primer print, incrementamos $var, pero al evaluar antes del incremento obtenemos su valor original, sin embargo, para evaluaciones posteriores su valor ha sido incrementado.

** Es el operador de Exponenciacion, así 5**2 resulta en 25 (5 al cuadrado)

* Es el operador de Multiplicación

/ Es el operador de División

% Es el operador de Modulo

+ Es el operador de Suma

- Es el operador de Resta

A estos no les dedicare mayores explicaciones por ser típicos en todos los lenguajes de programación y comunes a la matemática cotidiana.

Sin embargo, hay interesantes derivados de estos que vale la pena revisar. Estos son los operadores de automodificación, estos se implementan al observar que en muchos programas se requiere asignar a una variable el resultado de una operación que involucra el antiguo valor de la misma variable. Estos operadores proporcionan una forma resumida de operar una variable con otra y asignar a la primera el resultado, por ejemplo:

Si queremos hacer un $total, que sea el $total mas un $cargo particular podríamos escribir:

$total=$total+$cargo;

Y en Perl (al igual que en C) tenemos la opción de resumirlo como:

$total+=$cargo;

Nótese que el operador empleado fue "+=" que indica que al valor actual de la variable, se le asignara su propio valor mas el resultado de la evaluación de la expresión a al derecha del operador.

Así como se puede formar el operador de asignación con incremento, se pueden utilizar la mayoría de los otros operadores susceptibles a actuar sobre una variable, la única excepción no intuitiva es el de sustitución de patrones en la variable, que usa una sintaxis distinta "=~" que revisaremos al final de esta sección.

2.3.2- Operaciones Lógicas

Indispensables para las condiciones de todo ciclo de control son las operaciones lógicas en los lenguajes de programación, y en este contexto me permitiré incluir las funciones de comparación.

Las operaciones de comparación entre valores se dividen en aritméticas y de cadenas, y hay que tener especial cuidado en utilizar la familia correcta so pena de obtener resultados no deseados.

Operación       Aritmética      Cadena         

Mayor que       >               gt             

Menor que       <               lt             

Mayor o igual   >=              ge             

Menor o igual   <=              le             

Igual           ==              eq             

Diferente       !=              ne             

Compara         <=>             cmp            


La operación "Compara" regresa un valor de -1,0 o 1 dependiendo si el valor a la izquierda es menor, igual o mayor (respectivamente) que el de la derecha del operador, nótese que si deseáramos evaluar esto de forma lógica seria equivalente al operador "Diferente".

Debemos recordar que Perl considera como verdaderos los valores no nulos (cadenas) y diferentes de cero.

Además, tenemos los operadores lógicos que permiten cambiar el valor de verdad de una expresión:

Operador   Función                                                        

!          Negación Lógica                                                

-          Inverso aditivo (cambia el signo de un numero)                 

~          Negación Bit a bit de un valor                                 

&&         Conjunción lógica de los operandos                             

||         Disyunción lógica de los operandos                             

&          Conjunción bit a bit de los operandos                          

|          Disyunción bit a bit de los operandos                          

^          Or exclusiva bit a bit                                         

..         Si evaluado como escalar regresa alternativamente verdadero y  
           falso. Como arreglo genera un arreglo a partir de los limites  
           de un rango.                                                   

not        Negación lógica de la expresión a su izquierda (muy baja       
           precedencia)                                                   

and        Conjunción lógica de baja precedencia                          

or         Disyunción lógica de baja precedencia                          

xor        Disyunción exclusiva lógica (precedencia mínima)                       


2.3.3- Operaciones con Cadenas

Así como los operadores aritméticos forzan a la variable a comportarse como de tipo entero o real, así los operadores de cadena forzan a los escalares a comportarse como cadenas, de modo que los números son automáticamente convertido en cadenas (son su precisión completa) al aplicarles estos operadores.

Las operaciones a realizar con cadenas, por lo regular se manejan como funciones en vez de como operadores, con la excepción de las siguientes:

Operador   Función                                                        

=~         Especifica la expresión a afectar por búsqueda de patrones     
           regulares (ver sección siguiente).                             

!~         Especifica la expresión a afectar por búsqueda de patrones     
           regulares, pero evalúa negando el resultado lógico de la       
           operación.                                                     

.          Concatenación, todas las cadenas operadas por "." forman una   
           sola cadena.                                                   

x          Es el operador de repetición, en contexto escalar toma la      
           cadena de la izquierda y la repite el numero de veces que el   
           numero de la derecha indica. En contexto de lista, toma la     
           lista a su izquierda y la repite las veces que indica el       
           operador a su derecha en una nueva lista.                      

++         Autoincremento, modifica el valor de los elementos de una      
           cadena de modo que se incrementen sin cambiar su naturaleza,   
           por ejemplo ++"zzz" -> "aaaa"; ++"99" -> "100" , ++"a9" ->     
"b0", etc.                                                     


2.3.4- Operaciones Regulares

Este sección se dedica a dar una rápida revisión a las operaciones de sustitución y reconocimiento de patrones (definidos por lenguajes regulares) que son de las características mas utilizadas y útiles de Perl, aun cuando no sean tan sencillos de comprender en un primer acercamiento, recomiendo al lector que dedique algún tiempo extra al estudio de la construcción de las expresiones para describir los patrones a buscar por su cuenta, pues puede obtener grandes beneficios de este esfuerzo, la referencia es hasta donde se, la mejor fuente de información a este respecto, en las secciones de operadores PERLOP y de expresiones regulares PERLRE.

Los operandos para utilizar las expresiones regulares son:

Operado Función                                                           
r                                                                         

=~      Especifica que la cadena a la izquierda del operando es la que    
        pasara por el proceso ya sea de sustitución, o de búsqueda.       

!~      Especifica que la cadena a la izquierda del operando será la      
        afectada, y hace que la expresión en su conjunto regrese el       
        negado del valor lógico resultante.                               

//      Delimita un patrón regular.                               

Por defecto, las operaciones de búsqueda de patrones y sustitución actúan sobre el parámetro default $_ , y al evaluarlas modifican su valor y regresan el numero de sustituciones u ocurrencias encontradas.

La formación y manejo de las expresiones regulares escapa a los alcances de este documento, así que solo mencionare como utilizar estos operadores para realizar substituciones y su derivación para buscar algún patrón.

Para realizar búsquedas, la sintaxis básica es:

s/PATRÓN/CADENA/g

Que hace la substitución de todas las ocurrencias de PATRÓN en CADENA. En esta forma, actuara sobre el parámetro default $_ y regresara el numero de substituciones que haga o cero si no encuentra el patrón.

Así, para cambiar todas la ocurrencias de "Hola" por "Saludos" en una cadena llamada $cadena (en vez de en $_) deberemos escribir:

$cadena=~s/Hola/Saludos/g;

Por el contrario, si deseamos saber cuantas veces aparece "Hola" en cadena y colocar este valor en $cuenta deberemos escribir:

$cuenta=( $cadena=~s/Hola/Hola/g );

Nótese que en realidad no realizo la operación de búsqueda (que si existe) sino la substitución por la misma cadena, para evitar introducir nuevas operaciones.

Por ultimo, si deseamos determinar si no se encuentra la cadena, (pues para determinar si se encuentra bastaría usar el valor de veces que se encontró la cadena como condición lógica, pues evaluara como verdadero si es distinto de cero y falso si es cero) usaremos el operador !~

if($cadena!~s/Hola/Hola/g)
{
  print "No se encontraron \"Hola\"s\n";
}

Como ya se menciono, para un tratamiento serio de las cadenas regulares y los operadores para utilizarlas hay que consultar la referencia de perl en las secciones de PERLOP y PERLRE.

2.3.5- Operaciones Misceláneas

A continuación describo brevemente la función de otros operadores que resultan útiles en condiciones particulares.

Operador Función                                                          

?:       El operador condicional, es una forma de if resumida en una      
         sola expresión, tiene tres operandos, op1?op2:op3 donde op1 es   
         una expresión que evalúa falsa o verdadera, Si evalúa            
         verdadera, se evaluara y la expresión regresara el valor de      
op2, si por el contrario, op1 evalúa falso, es op3 la que        
         determina el valor final de toda la expresión.                   

,        Como el operador , de C, si se usa entre dos expresiones,        
         evalúa la de la izquierda, desecha el valor obtenido y evalúa    
         la expresión de la derecha regresando el valor así obtenido.     

=>       Este operador funciona primordialmente igual que la coma, pero   
         es exclusivo de Perl 5, forzando en algunas versiones que el     
         operando de la izquierda sea una cadena (recuérdese su uso para  
         declarar arreglos asociativos).                                  


Además, tenemos los signos de puntuación, que a diferencia de otros lenguajes son considerados operadores de Perl.

Básicamente, tenemos 4 tipos de símbolos que nos sirven para agrupar otros símbolos para darles una interpretación especial, son:

Símbolo Función                                                           

''      Especifica valores literales (cadenas), no hace substituciones.   

""      Especifica valores literales, haciendo substituciones.            

``      Ejecución, ejecuta el comando contenido entre ` después de hacer  
        las substituciones indicadas.                                     

//      Suelen usarse para delimitar expresiones regulares, hace          
        substituciones con la operación s//.                              


Entiendase por substitución el reemplazar el nombre de variables por los valores de estas, así como los caracteres de escape; por ejemplo:

$saludo="echo hola";
print '$saludo'."\n";
print "$saludo"."\n";
print `$saludo`."\n";
print "adios\n";

Genera como resultados algo similar a:

$saludo
echo hola
hola
adiós

Como se puede ver , en todos los casos imprime un fin de línea "\n" después de cada cadena (o expresión), la primera, es, literalmente la cadena dada; la segunda, es la cadena donde se ha substituido el nombre de la variable $saludo por su valor; la tercera, es el resultado de la ejecución del comando especificado en la cadena, donde se substituyó el nombre de una variable por su valor.

Perl 4 no regresa valor cuando utilizamos las comillas para ejecutar un programa o comando, es decir, lo ejecuta sin capturar su salida, de modo que esta irá a la misma salida estandar del script que lo invocó.

Alguna experimentación con el uso de estos operadores es altamente recomendable, pues las convenciones de cada programa para entregar sus resultados a algún dispositivo o a alguna salida distinta de la standard pueden ocasionar resultados poco previsibles.

2.4- Estructuras de Control

Las estructuras de control básicas de Perl y su sintaxis son:

IF:
if (EXPRESIÓN) BLOQUE
if (EXPRESIÓN) BLOQUE else BLOQUE
if (EXPRESIÓN) BLOQUE elseif (EXPRESIÓN) BLOQUE ... else BLOQUE
WHILE:
WHILE (EXPRESIÓN) BLOQUE
ETIQUETA WHILE (EXPRESIÓN) BLOQUE
FOR
for (EXPRESION;EXPRESION;EXPRESION) BLOQUE
ETIQUETA for (EXPRESION;EXPRESION;EXPRESION) BLOQUE
FOREACH
foreach VARIABLE (LISTA) BLOQUE

EXPRESIÓN es toda instrucción de PERL que evalúa en un solo valor, básicamente se emplean como inicializaciones y condiciones de las estructuras.

BLOQUE es un conjunto de expresiones encerradas en {}, nótese que todos los bloques, sin excepción inician con { y terminan con }.

ETIQUETA se refiere a etiquetas en el código, estas son nombres seguidos de ":", que permiten identificar a algún bloque en particular.

Para modificar la ejecución de los ciclos tenemos las siguientes instrucciones:

next          Esta interrumpe el flujo del ciclo iniciando de inmediato   
ETIQUETA      la siguiente iteración. Si no se especifica la ETIQUETA     
              afecta al ciclo mas interno.                                

last          Esta interrumpe el flujo del ciclo y pasa el control a la   
ETIQUETA      primera instrucción después del bloque del ciclo.           

redo          Esta reinicia la ejecución del ciclo, sin reevaluar la      
              condición.                                                  


Los programas a lo largo de este documento son ejemplos del uso de estas estructuras de control, por lo que no pongo ningún ejemplo especifico en esta sección.

2.4.1- Manejo de subrutinas

Como en cualquier lenguaje estructurado, Perl tiene la capacidad de generar nuevas funciones o procedimientos, pero perl tiene varias peculiaridades en la forma de definir sus funciones,Ademas de ser estas las que sufrieron mas cambios entre la versión 4 y 5.

Características de las funciones en la versión 4 de Perl

Para su ejecución, se les tiene que señalar explícitamente, anteponiendo el símbolo de & al nombre de la función (algo similar a la clase de una variable), las referencias a una función eran siempre a través de typeglobs por lo que es incomodo e inusual usarlas. Reciben siempre un único parámetro, el arreglo default @_ en el cual debían ir todos los parámetros que de desearan, sin posibilidad forzar el paso de ciertos parámetros a la función, para obtener los valores se suele hacer un $parametro=shift @_; por parámetro.

Características de las funciones en la versión 5.002 y superiores

Debe notarse que en versiones anteriores de perl no todas están implementadas, en particular, los prototipos de función no están implementadas sino hasta la versión 5.002.

Si un procedimiento o función ya ha sido declarado y se ejecuta como instrucción en una línea puede omitirse el símbolo & al inicio de su nombre. Es mas, siendo que perl no requiere el uso de paréntesis al inicio y final de una función, los parámetros pueden seguir al nombre de la función como para cualquier otra función nativa de Perl.

La verdadera utilidad de declarar nuestras funciones es el reducir la cantidad y complejidad de la revisión debe hacer de los valores que recibe como parámetros (dejando a Perl una mayor parte del trabajo), por ejemplo, si construimos una función que forzosamente requiera una clase de valor como parámetro (por ejemplo, una referencia) podemos definirla de modo que solo acepte parámetros que cumplan con esta característica, de modo que perl genere un error en caso de que traten de pasarle un dato de tipo distinto, en vez de que nosotros tengamos que verificar si el escalar recibido corresponde o no a una referencia.

A continuación revisaremos la definición de las subrutinas y funciones, Perl no hace mayor diferencia entre estas, de hecho se manejan igual y usaremos los términos indistintamente a lo lago de esta sección.

Sintaxis básica:

Declaraciones; especifican que habrá una función de este nombre pero no especifican su código, requieren que posteriormente se haga la definición.

sub NOMBRE;             Con parámetros indeterminados (estilo Perl4)      

sub NOMBRE(PROTOTIPO);              Especificando clase de parámetros, Perl5.002 o    

Definiciones Se especifica el código de una función, y si se desea, también su nombre y prototipo (tipo y numero de parámetros).

sub NOMBRE BLOQUE;           Definición de la función, parámetros         
                             indeterminados                               

sub NOMBRE (PROTOTIPO)       Definición con parámetros                    
BLOQUE                                                                    

$subref=sub BLOQUE           Especifica código y regresa una referencia   
                             a el, en este caso $subref es una            
                             referencia a una función de parámetros       
                             indeterminados cuyo código esta contenido    
                             en BLOQUE. No lo discutiremos a detalle por  
                             considerarlo una aplicación especial de      
                             referencias y subrutinas.                    


Los elementos que empleo en esta definición son:

NOMBRE      Es un identificador valido para perl (palabra formada de      
            números, letras y bajo-guion con las mismas reglas que C,     
            Pascal y demás. Es el nombre con el que se podrá invocar la   
            función, si se omite, en vez de definir o declarar la         
            función se generara una referencia a una función anónima.     

BLOQUE      Es un bloque de código, al igual que en los ciclos de         
            control, siempre inician con "{" y terminan con "}", dentro,  
            puede haber cualquier serie de sentencias de perl, y          
            determina un espectro de validez para las variables que se    
            declaren locales con my.                                             

PROTOTIPO   Es una cadena que indica la clase y una característica de     
            cada parámetro que la función recibirá.                       


Comencemos revisando como crear un procedimiento que no tome parámetros.

sub sencillito
{
  print "Yo soy un procedimiento sencillito\n";
}

Este procedimiento se invocara con el identificador "sencillito" o "&sencillito" , ejecutara su código y terminara sin regresar un valor. Una versión de este que ahora es función (pues regresa un escalar) será:

sub sencillo
{
  print "Yo soy una función sencilla\n";
  return "sencilla";
  die("no me muero\n");
}

Este procedimiento, regresa la cadena "sencillo" después de imprimir el mensaje especificado en el print, nótese que despues del return no continua su ejecución y que al igual que "sencillito" no pone atención a los parámetros, que podrían de hecho, pasársele o no.

return es la palabra reservada que hace que un procedimiento o función termine y regrese un valor, si se usa el return sin especificar valor o si se llega al final del código sin encontrar return regresa un nulo.

Si deseamos tomar parámetros, pero no aun hacer uso de los prototipos, debemos tomar en cuenta que todos los parámetros vienen empacados en @_ el arreglo default, y que para obtenerlos podemos usar las siguientes expresiones:

($primero,$segundo,$tercero)=@_;	#Obtiene tres escalares del arreglo (nótese que si tercero fuese arreglo, absorbería el resto de los escalares en @_)
$primero=shift @_;
$segundo=shift @_;
$tercero=shift @_;	#También obtiene tres parámetros de @_, pero este también los retira de @_, pues el operador shift toma el primer elemento de una lista y lo elimina, regresándolo como resultado de la expresión.

La ventaja del segundo método es que se retiran de @_ los elementos que eran parámetros para la función que hacemos, de modo que @_ puede contener mas información útil para algún otro propósito.

Las referencias son de gran utilidad para manejar todo tipo de datos como parámetros a una función, de hecho, la única posibilidad de pasar un arreglo a un procedimiento sin que el arreglo pierda su identidad requiere el uso de referencias, aunque como veremos, este requisito puede esconderse al invocar la función (no así al implementarla).

De cualquier modo, si recibimos referencias como parámetros solo deberemos darles el manejo de escalares para extraerlas de @_ y usarlas como siempre.

La manera de "ocultar" la necesidad de referencias en el momento de invocar la función implica el uso de prototipos.

Los prototipos pueden ser de gran utilidad para evitar errores de programación al invocar funciones con los parámetros equivocados, por lo que a pesar de no ser un tema básico lo incluyo en este documento.

Las cadenas del prototipo se componen de los siguientes símbolos:

$    Escalar                                                              

@    Arreglo (toma todos los parámetros siguientes en el arreglo)         

%    Arreglo Asociativo (también absorbe todos los parámetros)            

\    El identificador del siguiente parámetro deberá iniciar cón ($,@ o %).                                              

;    Los siguientes parámetros son opcionales                             


Así por ejemplo, las siguientes definiciones especifican:

sub biescalar ($$);	#recive dos escalares
sub escarreglo ($@);	# toma el primer parámetro individual y todos los demás en un arreglo.
sub tomahash (%);	#los parámetros recibidos forman un hash
sub dosotresesc ($$;$);	#dos escalares obligatorios y un tercero opcional.
sub arreglos (\@$\@);   #un arreglo, un escalar y después otro arreglo, sin que se mezclen.

Debe notarse que si se utilizan los parámetros de clase arreglo y hash sin "\" estos serán el ultimo parámetro a recibir, pues tomaran el resto de los parámetros dentro de si, de modo que si se desea tomar mas de un arreglo o hash como parámetro se deberá especificar la clase con "\". Nótese que el uso de "\" tiene como efecto colateral que se recibe una referencia al arreglo, hash o escalar, la que esta garantizada a ser una referencia al tipo especificado; nótese que no por eso el parámetro que acepta la función es una referencia, por el contrario, debe ser una variable de la clase especificada.

Este mecanismo, nos permite controlar la clase de la variable que nos dá el velor que recibimos como parámetro.

A continuación, veremos un ejemplo sencillo del uso de prototipos y funciones en un programa que especifica una función que debe recibir dos escalares e imprime un saludo con ellos.

#!/usr/bin/perl
print "hola\!\n";
sub rutina ($$)
{  $nombre=shift @_;
  $direccion=shift @_;
  print "Hola $nombre\nVivies en $direccion\n";
  return 345;
}

print "Dame tu nombre\n";
$res[0]=<STDIN>;chop $res[0];
print "Dame tu direccion\n";
$res[1]=<STDIN>;
chop $res[1];
print rutina $res[0],$res[1] ."\n";

Revísese con cuidado este programa y experiméntese con el, de modo que el siguiente programa (una modificación del anterior que explota las características de prototipos y de referencias para recibir dos arreglos y un escalar como parámetro) pueda comprenderse con facilidad.

#!/usr/bin/perl
# Definición de la subrutina "rutina"
sub rutina(\@\$\@)
{
  ($runo,$rsaluda,$rdos)=@_;
  #(@uno+0>@dos+0)?print "uno mayor que dos\n":print "dos mayor o igual a uno\n";
  print "$$runo[0],\n";
  print "$$rsaluda\n";
  print "$$rdos[0],$$rdos[1],\n";
  return 345;
}

#Programa principal
#Primer nombre
@res=("Daniel","Sol","Llaven");
#Segundo nombre
@re=("David","Jonathan","Sol","Llaven");
$algo="hola";
print rutina(@res,$algo,@re) ."***\n";

Este segundo programa define una función con prototipos que utilizan extensivamente "\", de modo que no solo requiere que sus parámetros sean un arreglo, un escalar y un segundo arreglo, sino que estos deben ser variables de las clases @, $ y @ respectivamente (por ejemplo, no aceptara una cadena constante en vez de $algo en la ultima línea).

Observando el código, vemos que los parámetros se reciben como referencias y se usan como tales, pero que en el programa principal se pasan los parámetros como variables de los tipos especificados en el prototipo (de hecho, "rutina" no aceptará ninguna otra cosa).

Espero que este ejemplo ayude a entender los conceptos de prototipos y de subrutinas, en realidad, no suele hacerse hincapié en estos temas por no ser necesarios, dada la libertad que perl nos da, pero en el contexto de desarrollos medianos a grandes pueden ser muy valiosos para evitar errores de difícil detección.

En este capitulo, como en todos los demás, es indispensable que el lector haga programas de ejercicio de su propia inspiración con el tiempo y la libertad de probar y experimentar los temas que revisamos aquí.

2.5- Operaciones con archivos

Al menos en la UNAM, Perl se utiliza principalmente con dos propósitos, el primordial es para implementar interfaces para servicios de red, y el segundo, es el proceso sistemático de grandes archivos de texto (bitácoras de operación, control de usuarios, etc.) debido a su facilidad para el manejo de archivos y si flexibilidad en el manejo de datos capturados de textos (formato muy común de la información en UNIX, que tiene la única desventaja de hacer crecer un poco mas los archivos, con las ventajas de ser interpretables con herramientas mas variadas, e incluso a simple vista, con la posibilidad de comprimirse con gran eficiencia).

Debo aclarar desde ahora, que el manejo de archivos binarios, no es uno de los puntos fuertes de perl, y que aunque puede hacerlo, no solo no es común, sino que resulta mejor utilizar programas hechos en C para proveer la manipulación de este tipo de archivos, es por esto, que este tema solo lo tocaremos brevemente, y nos enfocaremos al uso de archivos de texto.

Las operaciones básicas para trabajar con archivos son:

open(VARCH,ARCHIVO);
<VARCH>;
close(VARCH);

Que respectivamente abren, leen, y cierran el archivo. Donde:

VARCH      Es la variable que identificara al archivo, no se le pone      
           identificador de clase y por convención, se utiliza con        
           mayúsculas.                                                    

ARCHIVO    Es el nombre del archivo a utilizar, precedido de símbolos     
           que indican el modo de uso del archivo.                        


2.5.1- Apertura y cerrado

Los símbolos con los que especificamos el modo de operación de un archivo son:

Símbolo Significado                                                       

>       El archivo se abrirá para escritura (borrándolo si existe)        

<       El archivo se abre para lectura                                   

>>      El archivo se abrirá para escritura agregando al final            

+<      El archivo se abrirá de lectura escritura                         

+>      El archivo se abre para lectura y escritura vaciandolo primero    


El uso de archivos de lectura y escritura lo revisaremos mas adelante, pues involucra algunos procesos adicionales al manejo convencional.

Los "archivos" que no requieren ni abrirse (ni cerrarse) son, como es de esperarse, la entrada estándar, la salida estándar y la salida de errores, sin embargo, si pueden ser cerrados y abiertos con open y close, para esto se utilizan como nombre de los archivos (sin mas símbolos):

>-    Abre la salida          
      estándar                

-     Abre la entrada         
      estándar.               


Si no se utiliza ningún símbolo sino solo el nombre del archivo a utilizar, este archivo se abre para lectura.

La función open regresa al evaluarse el valor de la variable de archivo, o nulo si no se puede abrir el archivo en el modo deseado, de modo que si se utiliza como condición lógica resultara verdadera en caso de lograr abrir el archivo.

Algunos ejemplos de apertura de archivos son:

open(ETCP,"</etc/passwd");

Apertura de lectura únicamente del /etc/passwd asociado a la variable de archivo ETCP

open(SAL,">salida.txt");

Creación del archivo "salida.txt" en el directorio desde donde se invoque el programa, solo de escritura y asociado a la variable SAL.

El cerrado de los archivos en una operación recomendable, pero no necesaria, en el caso de los archivos, garantiza que todos los buffers han sido vaciados (o regresa falso en caso de que haya algún error).

2.5.2- Lectura de datos

La lectura de la información contenida en los archivos, como ya habíamos mencionado antes, se realiza con la operación <>, en esta ocasión la veremos mas a detalle.

El operador <> realiza la lectura de texto de el archivo, cuya variable se coloca entre < y >. Ahí es evaluado en un contexto de escalar, regresa un renglón, incluyendo el carácter de fin de línea. Si se evalúa en contexto de lista, regresa un arreglo de todos los renglones del archivo incluyendo sus terminadores de línea.

A manera de ejemplos:

$reng=<ENTRADA>;

Carga una línea del archivo asociado a ENTRADA (que debe ser de lectura).

@contenido=<ENTRADA>;

Toma todas las líneas del archivo y las coloca como elementos consecutivos del arreglo @contenido.

Debe mencionarse, que cuando se evalúa el operador <> para un archivo, el puntero de posición en el archivo va avanzando, de modo que en aplicaciones sucesivas (de contexto escalar) recibiremos renglones sucesivos. Y si se aplica en contexto de arreglo, una evaluación sucesiva no regresara mas renglones.

Además, el operador <> tiene la propiedad de tomar valor por defecto cuando no se especifica que archivo debe usar, esta característica tiene dos modalidades:

Usar el ultimo archivo abierto:

El operador <> continua trabajando con el ultimo archivo al que reavisamos, por ejemplo:

open(ENT,"<entrada.txt");	#trabaja con el archivo entrada.txt
while(<>)			#para toda línea del ultimo archivo abierto
{
  print;			# se imprime.
}

En este programa utilizamos varios parámetros default de las funciones:

en el while(<>) vemos que el operador <> no tiene ningún identificador de archivo, y sin embargo funciona como si se hubiese puesto <ENT> en vez de <>, esto es debido a que <> toma por default al ultimo archivo abierto como archivo default.

En segundo lugar, vemos que ni asignamos el valor que regrese <> a ninguna variable ni especificamos que variable debemos imprimir en el print. En ambos casos se esta utilizando el parámetro default $_, <> por defecto asigna el valor que resulte a $_ y print por defecto imprime a la salida estándar el valor de $_.

El otro modo de obtener los valores de archivos por defecto a trabajar con <>; supongamos un programa de nombre imprime.pl con el siguiente código:

while(<>)	#para toda línea del archivo default
{
  print;	#imprimirla
}

Aparentemente este programa carece de función pues no especifica ni abre ningún archivo para leer, el modo de especificar que archivo debe usarse es por medio de parámetros, de modo que el programa debe ejecutarse como:

imprime.pl entrada.txt

Para que imprima todas las líneas del archivo entrada.txt.

En resumen, si no se especifica cual archivo deberá de usarse para leer información con la operación <> Perl tratara de utilizar:

  1. El ultimo archivo abierto por open

  2. El siguiente parámetro como nombre de archivo a abrir y procesar como de lectura.

Nótese que si al segundo programa, imprime.pl, se le da mas de un parámetro, recorrerá todos los parámetros como nombres de archivos a abrir para usar con <>. Esto provee un método muy breve para hacer programas que procesen los archivos cuyos nombres se pasen como parámetros.

Deba tenerse cuidado al emplear las variables de default en un programa, y no recomiendo que se usen en programas grandes o de lógica compleja, pues estos valores son fácilmente afectables por otras operaciones y se requiere un conocimiento muy profundo de perl para conocer todas las peculiaridades del lenguaje en cuanto a estas variables especiales. Así pues, recomiendo usarlas solo para hacer mas simples los programas que ya sean sencillos por si mismos.

2.5.3- Escritura

Una vez que hemos abierto un archivo para escritura, la forma mas común de escribir en el, es mediante la función print, de hecho, print esta diseñada para escribir a archivos, pero el archivo que utiliza por defecto para escribir es el de la salida estándar STDOUT, la sintaxis para especificar que archivo debe utilizarse es:

print ARCHIVO LISTA

donde:

ARCHIVO es la variable de tipo archivo donde se dirigirá la salida

LISTA es una lista con los valores a imprimir, (si solo es un escalar se interpreta como una lista de un solo elemento, pero evita que al imprimir listas se obtengan los números de su cardinalidad en Perl 5).

Si se desea emplear las variables de archivo guardadas en arreglos, hashes o como resultado de expresiones se les deberá poner en un bloque de código (entre {}) para mas información consultar la función print en la referencia de Perl (sección PERLFUNC).

Así pues, para crear y llenar un archivo con los primeros 100 números enteros:

open(AS,">enteros.100");
for($c1=0;$c1<100;$c1++)	# de 0 a 99 (perdón si 0 no es entero)
{
  print AS "$c1\n";		#escribe al archivo de AS el numero.

  #contenido en $c1 y fin de línea.
}

Nótese que si se usan paréntesis alrededor de los parámetros del print muy probablemente Perl pensara que toda la expresión entre paréntesis especifica el valor a imprimir resultando en un error, por lo que recomiendo se evite el uso de paréntesis cuando se usa print para escribir a archivos.

2.5.4- Saltos y proceso binario

Antes que nada debo indicar que las funciones que revisaremos aquí las incluyo, no por considerarlas básicas para la programación, sino por su gran utilidad, y para que se conozca el modo en que perl puede lidiar con archivos binarios.

Sin embargo, si se pretende desarrollar algo usando estas herramientas recomiendo ampliamente una saludable dosis de experimentación y prototipaje para evitar demoras posteriores, además, será indispensable tener la referencia a su lado.

Así que siéntase el lector con la libertad de saltar esta sección y continuar con la 2.6, que le resultará mas útil al corto y mediano plazo.

Comencemos por la forma en que perl 5 (todo lo que tratare en esta sección no funciona o funciona de modo muy distinto en perl 4) puede leer, escribir y manejar información binaria, que llamaremos empaquetada:

Perl maneja la información en tipos abstractos, que nos ahorran gran trabajo de programación al momento de usarlos, desafortunadamente, esto no ayuda en el momento de querer leer tipos de datos que vienen como una serie de bits dentro de bytes. La solución que perl da a este problema es mas o menos sencilla, pero requiere algún entendimiento de ,que es una cadena.

Una cadena es una serie de caracteres, como el lector debe ya saberlo, pero cada carácter es en sí mismo representado por un byte; así que si no somos muy exigentes con los caracteres que pueden o no ser incluidos en una cadena, podríamos decir que cualquier serie de valores representados en bytes pueden interpretarse como una cadena. Pues bien, este es el concepto que Perl explota.

Un empacado de información lo manejaremos como un escalar que para fines prácticos es de tipo cadena. Pero que en vez de generar con el operador "." y contener texto común y corriente, contiene una serie de caracteres (que no necesariamente producirán resutlados coherentes si tratamos de imprimirlos) que en si mismos contienen información que nos interesa recuperar.

El proceso de desempacado de la información consiste en indicar a perl que a partir de una cierta cadena empacada debemos extraer un arreglo de valores a partir de un formato que deberemos especificar. La contraparte de este proceso es el generar nuestra cadena empacada a partir de un arreglo de valores y la misma información de formato que pretendemos leer después.

Bajo este enfoque, Perl puede manejar perfectamente bien un archivo basado en registros, pero tendría serias dificultades para manejar un archivo que no codifique toda su información en un patrón fijo, para esto será muy importante la capacidad de brincar en la posición del archivo.

La sintaxis de las funciones que empaquetan y desempaquetan la información son las siguientes:

pack($FORMATO,@VALORES);

Esta función regresa la cadena empacada con el formato descrito por la cadena $FORMATO que contiene los valores presentes en el arreglo @VALORES.

unpack($FORMATO,EXPRESION);

Esta es la contraparte de pack, usando el formato especificado por $FORMATO extrae los valores de la cadena que resulte de EXPRESIÓN (probablemente la variable que contiene la variable) y regresa al evaluarse un arreglo de valores de los tipos especificados.

Para especificar el formato en el que se empaquetara o con el cual desempaquetar la información se usa la siguiente sintaxis:

Simbolo  Significado                                                      

A        Cadena ASCII rellenada con espacios a la derecha.                

a        Cadena ASCII rellenada de nulos a la derecha.                    

b        Cadena de bits (Orden ascendente de bits)                        

B        Cadena de bits (Orden descendente de bits)                       

h        Cadena de nibbles (en hexadecimal con el nibble bajo primero)    

H        Cadena de nibbles (en hexadecimal con el nibble alto primero)    

c        Un valor de carácter con signo                                   

C        Un valor de carácter sin signo                                   

s        Un entero corto con signo                                        

S        Un entero corto sin signo                                        

i        Un entero con signo                                              

I        Un entero sin signo                                              

l        Un entero largo con signo                                        

L        Un entero largo sin signo                                        

f        Un punto flotante en formato nativo de Perl                      

d        Un doble en formato nativo de Perl                               

x        Un byte nulo                                                     

X        Regresar un byte                                                 

@        Llenar de nulos hasta la posición absoluta                       


Después de cada uno de estos se puede colocar un numero que indicara cuantos elementos de este tipo esperamos, a excepción de los del primer bloque (cadenas) donde el numero indicara la longitud de la cadena a interpretar como un solo elemento.

Todos estos especificadores funcionan tanto a la entrada como a la salida (e idealmente usamos la misma cadena de formato en el empaquetado y en el desempaquetado.

Así por ejemplo, para leer de una cadena empaquetada los bits, suponiendo que estos estar ordenados del menos significativo (al principio) al mas significativo (al final), y que buscamos leer 32 bits será:

($cadbit)=unpack("b32",$paquete);

Nótese que al escribir ($cadbit) estamos asignando al escalar $cadbit el primer elemento del arreglo generado por unpack, es decir, el primer elemento generado.

Por otro lado, para empaquetar y desempaquetar en una cadena 4 enteros podemos escribir:

$paquete=pack("i4",(34,-43,127,512));
@enteros=unpack("i4",$paquete);

Debe recordarse que con las cadenas el numero que se pone a continuación del carácter (aAbBhH) indica la longitud de las cadenas, pero con los demás tipos indica cuantos elementos de ese tipo esperamos.

Ahora, para leer byte por byte un archivo (los cuales podemos concatenar con los el acostumbrado operador ".") utilizamos la función getc

getc ARCHIVO;

Esta función regresa el siguiente carácter del archivo al que ARCHIVO hace referencia, o una cadena vacía (falso lógico) si llegamos al fin de archivo.

Por ejemplo, hagamos un diminuto programa que nos muestre los valores hexadecimales y binarios de todos los bytes de un archivo:

#!/usr/bin/perl
#programa de prueba de las capacidades de desempaquetado 
#y proceso binario de archivos en Perl5
open(AE,"<$ARGV[0]");
$contador=0;
print "offset\thexa\tbinario\t\tcaracter\n";
while($ch=getc(AE))
{
  $binario=unpack("B8",$ch); #deseamos el bit mas significativo a la izquierda
  $hexa=unpack("H2",$ch); #nibble mas significativo primero
  print "$contador\t$hexa\t$binario\t\"$ch\"\n"; #$ch es el carácter original
  $contador++;
}
close(AE);

Este programa también intenta imprimir el carácter leído con fines didácticos, si se emplea con archivos auténticamente binarios recomiendo que se elimine $ch de las variables a imprimir.

La característica comúnmente utilizada del proceso de archivos binarios que resta por revisar es la lectura aleatoria de archivos; esto se logra cambiando arbitrariamente la posición del apuntador de archivo mediante la función search. Esta función trabaja por bytes, de modo que es de muy poca utilidad cuando trabajamos con archivos de texto. La sintaxis de la función es:

search ARCHIVO,POSICION,MODO

donde:

ARCHIVO     es la variable con la que hacemos referencia a un archivo     

POSICIÓN    Especifica el avance desde la posición que indique el modo    


MODO        1-Desde el inicio, 2-Desde la posición actual, 3-Desde el     
            final.                                                     

2.6- Operaciones con recursos del sistema

Una de las grandes ventajas de Perl sobre otros lenguajes es la facilidad con la que interactúa con otros programas y con el sistema (UNIX en particular pero cualquiera en lo general). De hecho, una de las grandes mejoras del Perl5 sobre el Perl4 es que tiene un proceso mucho mejor coordinado de programas concurrentes invocados desde Perl, por lo que recomiendo ampliamente se considere el desarrollar programas que usen intensivamente el sistema con Perl5 (o superior).

Por considerar esta solo una introducción planteare solo las formas mas básicas de interacción con el sistema, recomiendo ampliamente, que para las tareas especificas para las que se suelen usar llamadas al sistema se consulte la referencia de Perl en la sección PERLFUNC para averiguar si están presentes y cual es la manera de usarlas.

En esta sección revisaremos tan solo los métodos de ejecución de programas comandos del sistema operativo que dividiremos en tres categorías:

  • Como expresiones a evaluar: Ya lo hamos revisado en la sección de operadores, pero conviene repetirlo en este contexto.

  • Sin interrelación: Invocación de comandos y programas esperando su culminación antes de terminar, o de modo que sean la ultima acción de nuestro programa.

  • Entubados: Principalmente mediante el uso de open y close, nos permiten alimentar de información otros procesos o alimentar nuestro programa del resultado de ellos.

2.6.1 Como expresiones

Como ya vimos, con el uso de las comillas `` podemos tratar un comando del S.O. como si fuese una expresión la cual nos regresa los resultados que dicho comando entrega a su salida estándar.

algunos ejemplos de esto son:

`date`

evalua como "Thu Sep 12 18:49:47 CST 1996\n" suponiendo que esa fuese la fecha

`wc -l Hola.pl`

evalua como " 3 Hola.pl" suponiendo que Hola.pl tuviese tres líneas.

Es la forma mas sencilla de ejecutar comandos desde nuestro programa en Perl.
Debe aclararse que Perl 4 solo ejecuta el comando dejando la salida estandar de este a la salida del script de Perl; de modo que no regresa ningun valor a nuestro script de Perl 4.

2.6.2 Sin interrelación

Por interrelación entiendase que: Ya sea que la entrada estándar del proceso ejecutado sea tomada desde nuestro programa. O que nuestro programa tome la salida del comando, de modo que en estos métodos la salida estándar del programa ejecutado será la salida estándar que nuestro propio programa tiene (consola por lo regular) y lo mismo para la entrada estándar.

Hay dos métodos para invocar programas con estas características "system" y "exec", y la diferencia entre estos radica en el ambiente que el programa invocado ocupara. La sintaxis de estas es:

system(PROGRAMA);

Ejecuta PROGRAMA como un nuevo proceso, espera a que acabe y continua

exec(PROGRAMA);

Ejecuta PROGRAMA, y termina nuestro script siendo reemplazado por el PROGRAMA que ejecutamos.

donde PROGRAMA es una cadena conde esta el comando (como lo daríamos en S.O.) a ejecutar, puede ser un comando compuesto, por ejemplo:

$comando="cat Hola.pl |grep daniel";
system($comando);#equivalente a system("cat Hola.pl |grep daniel");

efectivamente escribirá en la salida estándar las líneas del script Hola.pl en las que aparezca la palabra "daniel".

Cuando un programa se ejecuta, las variables de ambiente, el camino actual y otras condiciones del sistema operativo suelen ser determinantes para su desempeño, cuando utilizamos system, el programa a ser ejecutado se ejecuta en un nuevo ambiente (que no tiene mas relación con el nuestro que gozar de los mismos privilegios), mientras que cuando lo ejecutamos con exec el programa de hecho substituye a nuestro script de perl en el ambiente, de modo que hereda todas las características que nuestro script haya determinado del ambiente.

De este modo, una posible aplicación de exec es cuando debemos forzar que el programa que ejecutamos tome variables de ambiente que colocamos con el script de perl, en cambio system es necesario cuando debemos ejecutar mas de un programa, o cuando debemos realizar mas acciones con nuestro script una vez que el programa invocado termine.

2.6.3 Entubados

En esta sección, veremos como podemos capturar la salida que los programas que ejecutemos generan y procesar esta como si fuese un archivo y por otra parte, como podemos asignar una variable de archivo a la entrada estándar de un programa que ejecutemos para que procese lo que le alimentemos.

Una nota importante: Cuando ejecutamos programas de este modo Perl no espera a que el programa termine para continuar, si deseamos que lo haga debemos usar close con la variable de archivo que les asociemos para indicar que el programa debe terminar (y perl efectivamente esperara a que lo haga).

La ejecución de comandos o programas asociándolos a archivos se hace añadiendo el carácter "|" al comando a ejecutar y utilizando el comando open.

Por ejemplo, supongamos que deseamos procesar el resultado de un programa llamado "fuente". Si el comando para su ejecución desde sistema operativo fuese:

fuente saluda 3

deberemos "Abrirlo" de la siguiente forma:

open(RESF,"fuente saluda 3|");
while(<RESF>)
{
  #procesar los renglones que va poniendo en $_
}
close(RESF);	#espera a que termine y cierra el flujo de datos

Nótese como se coloco el carácter "|" al final del comando usual. Y que RESF funciona como un archivo abierto para lectura. También es conveniente recalcar que si deseamos ejecutar un comando que per se utilice entubamientos se puede hacer, por ejemplo:

open(ALGO,"fuente hola 3|prcesa_hola |");

Asociara a ALGO lo que el programa procesa_hola genere en su salida estándar, mientras que el S.O. ha entubado la salida de "fuente" a la entrada de "procesa_hola".

Del mismo modo, si deseamos poner información a la entrada estándar de un comando deberemos abrirlo colocando el símbolo "|" como primer carácter del comando. Y el archivo que asociemos se deberá usar como una archivo abierto para escritura.

De nuevo, cuando indiquemos el close, Perl enviara un EOF al archivo al que estamos ejecutando y esperara a que este le indique el termino de su proceso.

 

3- PROGRAMACIÓN ESPECIAL

En la Seccion 2, revisamos la programación básica en Perl, pero la programación en este lenguaje se basa, sobretodo, en gran cantidad de optimizaciones y formas alternativas de realizar tareas comunes lo que le dá una funcionalidad adicional que lo hace en extremo valioso en la realización de estas tareas o aquellas que las requieran.
Por eso es que dedico toda una sección de este tutorial a tratar estas características y el modo de emplearlas, para que el nuevo programador, no solo pueda elaborar programas en Perl, sino que pueda explotarlo para facilitar la realización de sus tareas mas usuales.

3.1- Uso de Perl en línea, Tareas Comunes

Una de las características de Perl dada su semi naturaleza de interprete es que puede ser ejecutado y actuar como un interprete de comandos (aunque los ejecuta hasta el momento de terminar todas las instrucciones con un fin de archivo desde teclado.

Además, Perl tiene varias opciones en línea que son de extraordinaria utilidad, por ejemplo, para substituir cadenas a lo largo de todo un archivo, o indicar que los errores se expresen en un formato mas entendible (en caso de que no este como default desde que lo compilamos), etc.

Esta sección, se dedica a revisar algunos de estos parámetros de Perl enfocado a usos específicos.

3.1.1- Formas de Especificar el programa

Existen tres métodos de indicar a Perl cual ha de ser el script a ejecutar:

1- Ejecutando un script directamente que especifique la colocación de Perl con una línea que inicie con "#!"

2- Pasando el script por la entrada estándar a Perl (completo con todo y fin de archivo), Pero esta opción no admitirá parámetros para el script.

3- Especificado las instrucciones en línea de comando con el interruptor -e. Esta la revisaremos con cuidado cuando veamos los interruptores.

3.1.2- Interruptores

En esta sección no intento revisar todos los interruptores descritos en la sección PERLRUN de la referencia, sino solo algunos de ellos enfocados a comprender mejor el como se realizan algunas tareas con Perl en línea de comando, y el orden también ha sido modificado tratando de hacer mas fácil su comprensión.

-e comando

Este interruptor se usa para indicar una línea de código en Perl a ejecutar (nota solo una, completa con ";"), si se da este interruptor, Perl no buscara un nombre de script en la lista de argumentos ,ni la recibirá por la entrada estándar. Para formar un script de varias líneas, se puede dar varias veces este interruptor con líneas distintas.

-n 

Hace que Perl asuma que existe el ciclo siguiente alrededor del script que le damos:

while(<>)
{
   #aqui va el script
}
-p

Al igual que "-n" hace que Perl asuma un ciclo alrededor del script, pero además, hace que imprima automáticamente el ultimo valor evaluado de cada ciclo del script, lo que equivale al siguiente ciclo:

while(<>)
{
  #El script va aquí
} continue
{
  print;
}

El uso del interruptor -p inhibe al interruptor -n.

-a

Al estar usando un interruptor "-e" o "-p" hace que los renglones que se van leyendo en el ciclo del "-n" o "-p" pasen por un "split" con el delimitador especificado con el interruptor -F y sean asignados, ya como arreglos a @F, resultando en la siguiente estructura del while:

while(<>){
  @f=split(' ');
  #aqui va el script
}
-F

Con este interruptor determina una expresión regular la cual se usara como delimitado para el split del switch "-a", se puede poner el patrón entre "/" siendo estos ignorados. Nota: los interruptores -a y -F son exclusivos de Perl 5.

-iextencion

Especifica que los archivos procesados por "<>" sean editados en su lugar, esto es, que el archivo de entrada sea movido a otro archivo con la extensión especificada, y que en el archivo original se vayan escribiendo los resultados que generemos. Si no se especifica ninguna extensión, al final del proceso no habrá ningún archivo con la información original.

-v

Imprime la versión y nivel de parches del ejecutable de Perl.

-w

Imprime advertencias sobre identificadores que se usen solo una vez, escalares que se usan sin haber recibido valor, subrutinas indefinidas, archivos sobre los que intentemos de realizar operaciones para las que no fueron abiertos, etc. Se recomienda para diagnosticas problemas en los programas.

Habiendo revisado algunos interruptores, ahora vamos a ver algunos ejemplos de las cosas que podemos hacer con ellos.

3.1.3- Tareas Comunes de Perl en Línea de Comando

Entiéndase por Línea de Comando, que desde nuestro interprete de comandos, invocamos Perl con los parámetros adecuados para que realice una función sin necesidad de haber escrito un script, por supuesto, no son tareas muy complejas, pero llegan a ser deslumbrantemente útiles.

3.1.3.1- Reemplazar una cadena en un archivo por otra

Es muy común que, por ejemplo, deseemos reemplazar todas las ocurrencias de, digamos, un camino y nombre de programa, por otro en un archivo de texto que podría ser el código fuente de un programa o alguna otra cosa, muchos procesadores de palabra soportan este tipo de procesos, pero con Perl no se requieren herramientas extra y podemos trabajar de un solo golpe grandes cantidades de archivos.

el comando seria de la forma:

Perl -pi.bak -e "s/CADENA1/CADENA2/g;" archivo1 archivo2 archivo3

Recordando lo que significan los interruptores:

-p indica que alrededor de la instrucción tenemos un ciclo que revisara todas las líneas de todos los archivos que se pasen como parámetros a script, además, que los resultados se imprimirán (a la salida estándar). Como este interruptor no recibe valores, puede acompañarse de otro interruptor (en este caso, "-p -i" es equivalente a "-pi").

-i Los archivos procesados serán editados en línea, generando respaldos de estos con la extensión .bak, la salida estándar del bloque de "-p" se redirecciona a los archivos origianles, de modo que los resultados del script serán el nuevo contenido de los archivos originales.

-e El comando a ejecutar dentro del ciclo, y cuyo resultado será el nuevo contenido de los archivos es una substitución (usamos implícitamente el argumento default $_ para indicar sobre que se hace la substitución), el comando, es una substitución del patrón en toda la línea leída.

archivo1 archivo2 archivo3 Son los archivos que usa el ciclo de "-p" que, como en el caso de cualquier otro script que usa "<>" son recibidos como parámetros.

El resultado final es que generamos archivo1.bak, archivo2.bak, archivo3.bak y archivo1, archivo2 y archivo3 y en los últimos la CADENA1 ha sido reemplazada por CADENA2.

3.1.3.2- Imprimir algún campo de un archivo

En UNIX, gran parte de la información del sistema se almacena en archivos de texto con separadores para indicar campos, siendo cada renglón un registro, además, como un ejemplo sencillo, planteemos una ejecución en línea de Perl que imprima todos los logins del etc/passwd que pertenezcan a un cierto grupo, digamos, el 100.

Perl -anF: -e 'if($F[3]==100){print "$F[0]\n";}' /etc/passwd
 

WikiCiencia - Creative Commons 2000-2015 - Algunos derechos reservados - Términos y Condiciones

Firenox