"La Gaceta de Linux...¡haciendo de linux algo un poco más divertido!"


Aprendiendo Perl, parte 3

Por Ben Okopnik

Traducción al español por Amanda Kisuy Chi
el día 25 de Mayo 2001, para La Gaceta de Linux

La dificultad de enseñar Perl como un primer lenguaje de programación está en que
tus estudiantes no lo apreciaran hasta que comiencen  con su segundo lenguaje. Lo complicado de enseñar  Perl como segundo lenguaje es que no hay un primer lenguaje
conveniente para partir de allí.
 -- Larry Wall

Cuando dicen que Perl es un `lenguaje glue', lo que realmente dicen es que es bueno para limpiar los errores después de otros programas
-- Mark-Jason Dominus en comp.lang.perl.misc
 
 

Panoráma
Este mes veremos los condicionales y los contructores de ciclos, y  examinaremos unos cuantos de los scripts que usan. También exploraremos como trabajar con  variables Perl, y echaremos un vistazo rápido a la capturación de la entrada usuario. Una vez que entiendas esta parte, te sugiero practicar con un par de scripts
experimentales y jugar con ellos; seguro, que cometeras errores - pero de aquí en adelante, necesitarás sustituir tus lecturas para bajar y ensuciarte. Si no juegas, no puedes ganar.
 
 

Condicionales

Aquí están las declaraciones de condicionales que Perl usa; nada particularmente inusual, si estás acostumbrado a los condicionales en otros lenguajes. Perl revisa si la condición es verdadera o falsa, y bifurca la ejecución con base en eso.



if    ( traffic_light_is_red ) {     # Si la condición 1 es verdadera, has
           stop;                     # Acción 1
}
elsif ( traffic_light_is_yellow ) {  # Si la condición 2 es verdadera, has
      hit_the_gas;                   # Acción 2
}
else  {
                                     # En todos los demás casos, has
      proceed_with_caution;          # Acción 3
}

Nota que la cláusula "elsif" no es requerida; y tampoco lo es el "else". También observa que "else" es la "opción atrapa-todo": si ligth es cualquier cosa excepto rojo o amarillo - apagado, solo consigue rebajar por accidente, etc. - la acción es 'proceed_with_caution'.

A diferencia de C, cada acción debe ser encerrada en un bloque ( definido por los corchetes):

if ( $tomato eq "red" )   print "Ripe.\n";      # ¡INCORRECTO!
if ( $tomato eq "red" ) { print "Ripe.\n"; }    # Correcto


unless ( $blarg == $foo ) {          # Si la condición 1 es false, has
       print "Unequal!.\n";          # Acción 1
}
else   {                             # De otra forma, has
       print "They're equal.\n";     # Acción 2
}

Bastante obvio. Esto ayudará a pensar en "unless" como el condicional "si no". De nuevo, el "else" es opcional. No, no hay cosas como "elseunless". :)
 

Ciclos

Ah, ciclos maravillosos. Estas son las cosas que hacen que las acciones sucedan, tantas veces como queramos, basados en una condición. Podrías decir que los ciclos son la principal razón de las computadoras en general, su principal uso como las herramientas que son es: repetir trabajo. Aquí están los tres tipos más comunes de ciclos en Perl:



while ( $cat eq "away" ) {             # Mientras la condición 1 sea verdadera, has
      print "The mice will play.\n";   # Acción 1
}



until ( $time > 1159 ) {        # Mientras la condición 1 sea falsa, has
     print "It's morning.\n"    # Acción 1
}



El ciclo "for" puede ser implementado en dos formas diferentes -una es como el ciclo "for" en C:

for ( $n = 99; $n > 0; $n-- ) {
    print "$n bottles of beer on the wall, $n bottles of beer,";
    ...
}

En este caso,  ponemos $n con un valor inicial (99), decrementamos en 1 cada vez que entramos en el ciclo, y checamos para estar seguros que  $n sea menor que 0. Si no lo es, salimos del ciclo.

El segundo método,  es algo como el ciclo Clipper, FoxPro, etc. "foreach",  es por mucho el más común:

foreach $n ( 0..1000 ) {
        print "Day $n on this deserted island. So far, I've had ";
        print $n * 100, " bananas. I hope I'm rescued soon.\n";
        ...
}

Puede también ser usado de la siguiente manera:

for ( 0..1000 ) {
    print "Day $_ on this deserted island. So far, I've had ";
    print $_ * 100, " bananas. I hope I'm rescued soon.\n";
    ...
}

Nuestro viejo amigo, el "$_" (explicado en la parte previa de esta serie.). Efectivamente viene bien. Nota que "foreach" es solo un alias para "for", y que se pueden usar indistintamente.


Todos los condicionales y ciclos anteriores pueden también ser usados como modificadores de declaraciones solas, como sigue:

print "This is line $_ of 50.\n" for ( 1..50 );

Lo anterior imprimirá 50 líneas, numeradas en la forma obvia.

print "I've found him!" if /Waldo/;

La línea de arriba será impresa si el buffer default ($_) contiene  un "Waldo".

Un hecho interezante que combina bien con ciclos y condicionales es que variables vacias en Perl regresan un valor null - que es "false". Esto es perfecto para verificarlas:

print if $_;            # Imprime $_ si contiene algo

El siguiente ejemplo muestra que un valor cero tambien es falso:

print "5280 is true.\n" if 5280;   # Esto imprimirá.
print "0 is true.\n" if 0;         # Esto no imprimirá.

Aquí hay un ejemplo con una lista:

while ( @a ) {
      print pop @a;     # "Pop" el último valor de @a y lo imprime
      $count =  @a;     # Obtiene el número de elementos en @a
      print $count, " elements left in \@a.\n";
}

Cuando el último elemento se ha ido, el ciclo termina.

unless ( %hash ) {
       %hash = ( 'first' =>  'Mighty Joe',
                 'last'  =>  'Young',
                 'type'  =>  'gorilla',
                 'from'  =>  'Pangani Mountains',
                 'born'  =>  '1949',
                 'Mom'   =>  'Jill',
                 'Dad'   =>  'Gregg'
       );
}

Si "%hash" es vacio, lo poblamos con algunos valores iniciales.

El operador rango, que hemos usado un par de veces hasta ahora, es usado ampliamente: este te permite  especificar un rango de números o letras. Nota que los rangos tienen que ser del mismo 'tipo' - si especificas ( 'a'..'Z') o ('A'..'z'), la salida no será lo que esperas. Tampoco puedes especificar ( 'z'..'a'); porque tampoco trabajará. Sin embargo, hay una forma fácil de hacerlo:

foreach $letter ( reverse 'a'..'z' ) {
    print "$letter\n";
}

Esto tambien incrementa bien "letter lists":

for ( 'aa'..'zz' ) {
    print "$_ ";        # Imprimirá "aa ab ac ... zx zy zz"
}
 
 
 

Entrada Usuario
 

Capturar entradas del teclado, o entradas de STDIN en general -como las líneas conducidas a la entrada de nuestro script via alguna cosa como

cat file | perl_script

 - es fácil; eso es para lo sirve que el "operador diamante" de Perl.

while ( <> ) {        # Captura todo el teclado o los conductos de entrada
      print;          # Imprime cada línea tan largo como entrada exista
}

Lo anterior trabaja exactamente como "cat" -imprimirá toda la entrada conducida a el,  hará "cat" a un archivo si está corriendo con el nombre de archivo usado como argumento, y aceptará ( y resonará) la entrada usuario mientras que presiones Ctrl-D or Ctrl-C. Eso también puede ser escrito de la siguiente forma:

print while <>;

para una mayor sintáxis "Perl". Nota que "<>" y "<STDIN>" están relacionados pero no son equivalentes:

print while <STDIN>;

responderá a el teclado y los conductos de entrada, pero  no imprimirá el contenido de un archivo dado como un argumento. No he encontrado una situación en donde necesite esta función, así que simplemente uso "<>".

Si quieres la entrada usuario a una variable, Perl tambien hace esto fácilmente -pero hay un aspecto en el que necesitas ser cuidadoso:

$answer = <>;        # Obtiene la entrada, la asigna a una variableif    ( $answer eq "y" ) {
      print "Yes\n";
}
elsif ( $answer eq "n" ) {
      print "No\n";
}
else {
      print "No idea!\n";
}

El script anterior siempre imprimirá "No idea!" Hmm... se ve bien; ¿Cuál puede ser el problema?

El problema es que Perl captura cada cosa que le des. Así cuando tecleas "y", ¿Cuál es la siguiente tecla que presionas? ¡es "Enter" ! Así, la variable almacenada en $answer es NO "y", esto es "y\n" - la respuesta y el final de línea. ¿Cómo podemos trabajar con esto? Perl, por supuesto, tiene una función - una que debes usar siempre cuando obtienes la entrada usuario:

chomp ( $answer = <> );

"chomp" removerá el caracter de fin de línea, de la cadena a la que es aplicado. También quitará EOLs de cada elemento del arreglo que recibe como argumento. En la vieja versión Perl4, "chop" eliminaba el último caracter de un escalar (o de los elementos de el arreglo) sin importar lo que fuera; está aún disponible por si debes usarlo para tal propósito, pero para tomar la entrada usuario, use "chomp" (también conocido, por los mensajes de error de Perl, como "safe chop").
 

Ejercicios Para La Mente

Intente construir un par de scripts, tan solo para su propia educación y entretenimiento:

Un script que toma un número como entrada, e imprime "Hello!" muchas veces.

Un script que toma la hora actual (0-23) como entrada y dice "Good morning", "Dobriy den'", "Guten Abend", o "Buenas noches" como resultado. <grin>

Si implementas algo particulamente interezante, no dudes en mandarmelo para la siguiente parte de esta serie: obtendrás el crédito por escribirlo, lo analizaré gustosamente para ti, y los dos seremos micro-famosos y nos retiraremos a Belice en el proceso. <laugh>

No olvides que: tu línea shebang debe siempre contener "-w". Si no pides ayuda a Perl con tus errores, estarás perdiendo mucho tiempo. ¡Deja a la computadora hacer el trabajo difícil!
 

#!/usr/bin/perl -w
print "See you next month!"
 

Ben Okopnik
perl -we'print reverse split//,"rekcah lreP rehtona tsuJ"'


Referencias:

Páginas man relevantes de Perl(disponible en algún sistema pro-Perl-y configurado):

perl      - panorama                 perlfaq   - Perl FAQ
perltoc   - doc TOC                  perldata  - estructuras de datos
perlsyn   - sintáxis                 perlop    - operadores/precedencia
perlrun   - ejecución                perlfunc  - funciones constructoras
perltrap  - trampa para imprudentes  perlstyle - estilo guía

"perldoc", "perldoc -q" y "perldoc -f"


Copyright © 2001, Ben Okopnik.
Copying license http://www.linuxgazette.com/copying.html
Publicado en el número 65 de Linux Gazette, Abril 2001