Professional Documents
Culture Documents
Usando Subrutinas
Qu es una Subrutina
En Perl, 1 subrutina es una porcin de cdigo separada usada para efectuar una tarea
particular. El programa PERL ejecuta esa porcin de cdigo llamando o invocando la
subrutina.
Una Subrutina se usa para alguno de estos dos propsitos:
Para partir un programa en partes pequeas hacindolo mas fcil de leer y mas
entendible.
Para permitirnos usar una porcin de cdigo para llevar a cabo la misma tarea
varias veces eliminando la duplicacin.
1: #!/usr/local/bin/perl
2:
3: $total = 0;
4: &getnumbers;
5: foreach $number (@numbers) {
6: $total += $number;
7: }
8: print ("El total es $total\n");
9:
10: sub getnumbers {
1
11: $line = <STDIN>;
12: $line =~ s/^\s+|\s*\n$//g;
13: @numbers = split(/\s+/, $line);
14: }
SALIDA
$ program9_1
11 8 16 4
El total es 39
$
ANALISIS
Lneas 10-14 son un ejemplo de una subrutina. La palabra reservada (keyword) sub le
dice al Intrprete de Perl que eso es una definicin de una subrutina.
Antes de que la lnea 13 finalice, el Intrprete de Perl vuelve al main program y ejecuta
la lnea inmediatamente siguiente a la llamada de la subrutina, la cual es la lnea 5.
Las Lneas 5-7 suman los nmeros usando la sentencia foreach para loopear la lista de
nmeros almacenada en @numbers. (Ojo, el programa no chequea si son nmeros, si
fueran otra cosa los convierte a cero)
La sintaxis para la definicin de una subrutina es:
sub subname {
statement_block
}
subname es el nombre de la subrutina. Como todos los nombres en Perl, subname
consiste en un carcter alfabtico seguido de una o ms letras, dgitos o guiones (_). Es
aconsejable evitar nombres iguales al de una palabra reservada de perl, y no poner todo
el nombre con letras maysculas ya que as es como Perl llama a sus subrutinas internas.
2
statement_block es el cuerpo de la subrutina y consiste en una o mas sentencias Perl.
Cualquier sentencia que puede aparecer en un main program, puede aparecer en una
subrutina.
NOTA
El Intrprete de Perl nunca confunde un nombre de subrutina con el nombre de
una variable escalar o cualquier otro porque puede deducir del contexto cual
nombre estamos haciendo referencia. Pro lo tanto podemos tener una subrutina
y una variable escalar con el mismo nombre. Por ejemplo:
$word = 0;
&word;
Aqu, cuando el Intrprete de Perl ve el & en la segunda sentencia, asume que
esta llamando una subrutina llamada word.
Atencin
Cuando se definen nombres de subrutinas, es conveniente no usar nombres
iguales a las funciones de librera de Perl para evitar errores involuntarios. Si
bien nosotros podemos definir una subrutina llamada split y el Intrprete de
Perl puede distinguir la invocacin de la subrutina split de la funcin de
librera split, (porque el nombre de la subrutina est precedido por un &
cuando se invoca), es muy fcil olvidar tipear el & y caer en el error.
@words = &split(1, 2); # subrutina
@words = split(/\s+/, $line); # library function
3
Cuando readaline termina, la ejecucin del programa contina en la lnea 5. Cuando la
lnea 5 es ejecutada, el programa saltea las sentencias correspondientes a la definicin
de la subrutina y contina en la lnea 9. El cdigo dentro de una subrutina nunca es
ejecutado directamente.
TIP
Si bien las subrutinas pueden aparecer en cualquier parte de un programa, es usual
definirlas al principio o al final de un programa. Esto lo hace mas legible.
4
Si bien esta subrutina es util, tiene una seria limitacin: sobreescribe el arreglo
@numbers (tambien sobrescribe $line). Esto puede traer problemas Por ejemplo, si
consideramos lo siguiente:
@numbers = ("the", "a", "an");
&getnumbers;
print ("El valor de \@numbers es: @numbers\n");
1: #!/usr/local/bin/perl
2:
3: $total = 0;
4: @numbers = &getnumbers; # as getnumbers retornar su valor en @numbers
5: foreach $number (@numbers) {
6: $total += $number;
7: }
8: print ("El total es $total\n");
9:
10: sub getnumbers {
11: $line = <STDIN>;
12: $line =~ s/^\s+|\s*\n$//g;
13: split(/\s+/, $line); # Este es el return value
14: }
SALIDA
$ program9_3
11 8 16 4
El total es 39
$
ANALISIS
La lnea 4, nuevamente llama a la subrutina getnumbers, pero esta vez, el arreglo
@numbers es asignado a la lista de nmeros ledos desde el standard input file; de esta
manera la asignacin se hace en el cuerpo principal del programa, no en la subrutina,
esto lo hace mas fcil de seguir.
5
La lnea 13 es la ltima expresin evaluada en getnumbers, por eso automticamente se
convierte en el valor de retorno de la subrutina.
Se puede usar el valor de retorno de una subrutina en cualquier parte de una expresin.
Por ejemplo:
foreach $number (&getnumbers) {
print ("$number\n");
}
Este foreach itera sobre la lista de nmeros retornados por getnumbers. Cada
elemento de la lista es asignado a $number por turno, lo que produce la impresin de
todos los nmeros de la lista.
1: #!/usr/local/bin/perl
2:
3: $total = &get_total;
4: print("El total es $total\n");
5:
6: sub get_total {
7: $value = 0;
8: $inputline = <STDIN>;
9: $inputline =~ s/^\s+|\s*\n$//g;
10: @subwords = split(/\s+/, $inputline);
11: $index = 0;
12: while ($subwords[$index] ne "") {
13: $value += $subwords[$index++];
14: }
15: }
6
SALIDA
$ program9_5
11 8 16 4
El total es
$
ANALISIS
Obviamente que el propsito de Este programa es asignar el contenido de la variable
escalar $value a la variable escalar $total. Pero cuando la la lnea 4 trata de imprimir
el total, el valor de $total est vaco. Que sucedi?
1: #!/usr/local/bin/perl
2:
3: $total = &get_total;
4: print("El total es $total.\n");
5:
6: sub get_total {
7: $value = 0;
8: $inputline = <STDIN>;
9: $inputline =~ s/^\s+|\s*\n$//g;
10: @subwords = split(/\s+/, $inputline);
11: $index = 0;
12: while ($subwords[$index] ne "") {
13: $value += $subwords[$index++];
14: }
15: $retval = $value;
16: }
SALIDA
$ program9_6
11 8 16 4
El total es 39.
$
ANALISIS
Este programa es igual al 9.5 excepto por el agregado de la lnea 15: Esta lnea asigna
el total almacenado en $value a la variable escalar $retval.
7
La lnea 15 asegura que el valor de la ltima expresion evaluada en la subrutina
get_total contenga el total buscado. Luego la lnea 3 efecta una nueva asignacin e
imprime.
Pero en realidad no sera necesario efectuar la asignacin a retval, la subrutina
get_total podra quedar como sigue y seguir siendo efectiva:
sub get_total {
$value = 0;
$inputline = <STDIN>;
$inputline =~ s/^\s+|\s*\n$//g;
@subwords = split(/\s+/, $inputline);
$index = 0;
while ($subwords[$index] ne "") {
$value += $subwords[$index++];
}
$value;
}
Aqu la ltima expresin evaluada es simplemente $value. El valor de esta expresin es
el valor corriente almacenado en $value, es decir la suma de los nmeros de la lnea.
TIP
Las subrutinas que dan su valor de retorno al final (como get_total en Listado
9.6) son conocidas como single-exit modules. Estas subrutinas salvan problemas
como los vistos en Listado 9.5, y normalmente son mucho ms fciles de leer. Por
estas razones es una muy buena idea definirlas as.
La Sentencia return
Otra manera de asegurar el valor de retorno de una subrutina es usando la sentencia
return . La sintaxis es la siguiente:
return (retval);
retval es el valor que queremos retornar. Puede ser un valor escalar (incluido el
resultado de una expresin) o una lista.
Listado 9.7 da un ejemplo de su uso
Listado 9.7. Un programa que usa la sentencia return
1: #!/usr/local/bin/perl
2:
3: $total = &get_total;
4: if ($total eq "error") {
5: print ("No se ingres nada.\n");
6: } else {
7: print("el total es $total.\n");
8: }
9:
10: sub get_total {
11: $value = 0;
12: $inputline = <STDIN>;
13: $inputline =~ s/^\s+|\s*\n$//g;
14: if ($inputline eq "") {
15: return ("error");
16: }
17: @subwords = split(/\s+/, $inputline);
18: $index = 0;
8
19: while ($subwords[$index] ne "") {
20: $value += $subwords[$index++];
21: }
22: $retval = $value;
23: }
SALIDA
$ program9_7
^D
No se ingres nada.
$
ANALISIS
Este programa es similar al Listado 9.6. La nica diferencia de este programa es que
chequea si la lnea de input existe.
Si la lnea de input no existe, la expresin condicional en la lnea 14 es verdadera, y la
lnea 15 es ejecutada. La lnea 15 sale de la subrutina con return value error; Luego
error se asigna a $total en la lnea 3.
Este programa muestra tambin como es til permitir almacenar en variables escalares
nmeros o character strings. Cuando la subrutina get_total detecta el error, asigna un
valor no numrico a $total, lo que permite fcilmente determinar que algo anduvo
mal. En otros lenguajes, no se ofrece esta flexibilidad.
Usando variables locales en Subrutinas
La subrutina get_total en Listado 9.7 define algunas variables que solo se usan dentro
de la subrutina: El arreglo @subwords, y 4 variables escalares $inputline, $value,
$index, y $retval.
Si tenemos la certeza que estas variables solo sern usadas dentro de la subrutina,
podemos decirle a Perl que defina estas variables como locales (local variables).
En Perl 5, existen dos sentencias para definir local variables:
La sentencia my, la cual define variables que solo van a existir dentro de la
subrutina.
La sentencia local, la cual define variables que solo van a existir dentro de la
subrutina y cualquier otra subrutina llamada por esta (como hacer esto lo
veremos luego).
Listado 9.8 muestra como podemos usar my para definir variables que existan solo en la
subrutina.
Listado 9.8. Un programa que usa local variables.
1: #!/usr/local/bin/perl
2:
3: $total = 0;
4: while (1) {
5: $linetotal = &get_total;
6: last if ($linetotal eq "done");
7: print ("Total para esta lnea: $linetotal\n");
8: $total += $linetotal;
9
9: }
10: print ("Total para todas las Lneas: $total\n");
11:
12: sub get_total {
13: my ($total, $inputline, @subwords);
14: my ($index, $retval);
15: $total = 0;
16: $inputline = <STDIN>;
17: if ($inputline eq "") {
18: return ("done");
19: }
20: $inputline =~ s/^\s+|\s*\n$//g;
21: @subwords = split(/\s+/, $inputline);
22: $index = 0;
23: while ($subwords[$index] ne "") {
24: $total += $subwords[$index++];
25: }
26: $retval = $total;
27: }
SALIDA
$ program9_8
11 8 16 4
Total para esta lnea: 39
7 20 6 1
Total para esta lnea: 34
^D
Total para todas las Lneas: 73
$
ANALISIS
Este programa usa dos copias de la variable escalar $total. Una copia de $total esta
definida en el main program y almacena el total de todas las Lneas.
Las variables locales pueden aparecer en cualquier parte de un programa, pero para dar
claridad en el cdigo, es preferible ubicarlas al principio.
10
INICIALIZACIN DE VARIABLES LOCALES
Las variables locales pueden ser inicializadas. Ejemplo:
sub my_sub {
my($scalar) = 43;
my(@array) = ("here's", "a", "list");
# code goes here
}
Aqui la variable local $scalar toma un valor inicial de 43, y el arreglo @array es
inicializado con la lista ("here's", "a", "list").
Pasando valores a una Subrutina
Podemos hacer mas flexibles nuestras subrutinas si permitimos que ellas acepten
valores pasados desde el main program. Estos valores se los llama Argumentos.
Listado 9.9 provee un simple ejemplo de una subrutina que acepta tres argumentos
Listado 9.9. Un programa que usa una subrutina to print three numbers and their
total.
1: #!/usr/local/bin/perl
2:
3: print ("Ingrese 3 numeros, uno por vez:\n");
4: $number1 = <STDIN>;
5: chop ($number1);
6: $number2 = <STDIN>;
7: chop ($number2);
8: $number3 = <STDIN>;
9: chop ($number3);
10: &printnum ($number1, $number2, $number3);
11:
12: sub printnum {
13: my($number1, $number2, $number3) = @_;
14: my($total);
15: print ("Los numeros ingresados son: ");
16: print ("$number1 $number2 $number3\n");
17: $total = $number1 + $number2 + $number3;
18: print ("El total es: $total\n");
19: }
SALIDA
$ program9_9
Ingrese 3 numeros, uno por vez:
5
11
4
Los numeros ingresados son: 5 11 4
El total es: 20
$
ANALISIS
11
La lnea 13 define copias locales de las variables escalares $number1, $number2, y
$number3 al mismo tiempo que asigna el contenido de la variable de sistema @_.
@_ es creada cada vez que una subrutina es llamada con argumentos. Contiene una lista
con los argumentos en el orden en el cual fueron pasados. En este caso, @_ contiene la
lista (5, 11, 4).
Listado 9.10 es otro ejemplo de un programa que pasa argumentos a una subrutina. Este
programa usa la misma subrutina para contar el numero de palabras y el numero de
caracteres que recibe por STDIN.
Listado 9.10. Otro ejemplo de una subrutina con argumentos pasados.
1: #!/usr/local/bin/perl
2:
3: $wordcount = $charcount = 0;
4: $charpattern = "";
5: $wordpattern = "\\s+";
6: while ($line = <STDIN>) {
7: $charcount += &count($line, $charpattern);
8: $line =~ s/^\s+|\s+$//g;
9: $wordcount += &count($line, $wordpattern);
10: }
11: print ("Total: $wordcount palabras, $charcount caracteres\n");
12:
13: sub count {
14: my ($line, $pattern) = @_;
15: my ($count);
16: if ($pattern eq "") {
17: @items = split (//, $line);
18: } else {
19: @items = split (/$pattern/, $line);
20: }
21: $count = @items;
22: }
12
SALIDA
$ program9_10
Esta es la lnea de input.
Aqui hay otra linea.
^D
Total: 10 palabras, 46 caracteres
$
sub addlist {
my (@list) = @_;
$total = 0;
foreach $item (@list) {
$total += $item;
}
print ("The total is $total\n");
}
Invocaciones ejemplo:
&addlist (@mylist);
&addlist ("14", "6", "11");
&addlist ($value1, @sublist, $value2);
En cada caso, los valores son juntados en una sola lista, y pasados como una lista simple
a addlist.
Como los valores son juntados, entonces solo se debe incluir una lista en los argumentos
de una sub y es conveniente ponerla COMO ULTIMO ARGUMENTO
La subrutina:
sub twolists {
my (@list1, @list2) = @_;
}
No es til porque las dos listas se juntan y SIEMPRE la se asigna la lista vaca a
@list2, ya que @list1 absorbe TODO el contenido de @_.
Otro Ejemplo:
sub twoargs {
my ($scalar, @list) = @_;
}
Invocacin:
&twoargs(47, @mylist);
13
EL VALOR 47 es asignado a $scalar, y @mylist es asignado a @list.
Si queremos podemos invocar a twoargs con una lista simple como sigue:
&twoargs(@mylist);
Con el mismo resultado que antes, el primer elemento de la lista se asigna a $scalar, y
el resto a @list.
Llamando Subrutinas desde otras Subrutinas
En Perl, podemos llamar subrutinas desde otra subrutina. Para hacerlo, se usa la misma
sintaxis que vinimos empleando.
Las Subrutinas que son llamadas desde otras subrutinas se las conoce como nested
subrutinas
Listado 9.11. Ejemplo de nested
1: #!/usr/local/bin/perl
2:
3: ($wordcount, $charcount) = &getcounts(3);
4: print ("Totals for three lines: ");
5: print ("$wordcount words, $charcount characters\n");
6:
7: sub getcounts {
8: my ($numLines) = @_;
9: my ($charpattern, $wordpattern);
10: my ($charcount, $wordcount);
11: my ($line, $linecount);
12: my (@retval);
13: $charpattern = "";
14: $wordpattern = "\\s+";
15: $linecount = $charcount = $wordcount = 0;
16: while (1) {
17: $line = <STDIN>;
18: last if ($line eq "");
19: $linecount++;
20: $charcount += &count($line, $charpattern);
21: $line =~ s/^\s+|\s+$//g;
22: $wordcount += &count($line, $wordpattern);
23: last if ($linecount == $numLines);
24: };
25: @retval = ($wordcount, $charcount);
26: }
27:
28: sub count {
29: my ($line, $pattern) = @_;
30: my ($count);
31: if ($pattern eq "") {
32: @items = split (//, $line);
33: } else {
34: @items = split (/$pattern/, $line);
35: }
36: $count = @items;
37: }
SALIDA
$ program9_11
This is a line of input.
Here is another line.
14
Here is the last line.
Totals for three Lines: 15 words, 70 characters
15
Subrutinas Predefinidas
Perl considera las subrutinas cuyo nombre est todo en maysculas como parte del
ncleo, y son llamadas automticamente por el sistema
BEGIN subrutina, se llama ANTES que cualquier sentencia. Es ms, se ejecuta
ANTES que los script regulares sean analizados.
END subrutina, se llama ANTES que termine el script regular, al mismo tiempo
que sale del intrprete de perl
Ejemplo de BEGIN
BEGIN {
Ejemplo de END
END {
die("Prepare to die!\n");
END {
Salida:
Prepare to die!
16
NOTA
Se pueden definer varias subrutinas END, en ese caso, se ejecutan
en el orden inverso de apariencia (la ultima se ejecuta primero)
AUTOLOAD
1: #!/usr/local/bin/perl
2:
3: ¬here("hi", 46);
4:
5: AUTOLOAD {
8: }
SALIDA
$ program9_15
arguments passed: hi 46
TIP
AUTOLOAD es til para organizar el Programa Perl en mdulos
17
QUE ESTA MAL?
1. #!/usr/local/bin/perl
2. #!/usr/local/bin/perl
$line = <STDIN>;
@words = split(/\s+/, $line);
$searchword = <STDIN>;
&search_for_word (@words, $searchword);
sub search_for_word {
local (@searchlist, $searchword) = @_;
foreach $word (@searchlist) {
return (1) if ($word eq $searchword);
}
$retval = 0;
}
3. #!/usr/local/bin/perl
$line = <STDIN>;
@words = &split_la lnea($line);
print ("@words\n");
sub split_la lnea {
local ($line) = @_;
local (@words);
@words = split(/\s+/, $line);
if (@words == 0) {
@words = ("empty list");
}
}
18