Toda la información sobre la subrutinas está (en inglés) en
perlsub.
En concreto, la forma de llamar a las subrutinas se puede hacer:
- Código: Seleccionar todo
NOMBRE(LISTA); # & es opcional con paréntesis.
NOMBRE LISTA; # Paréntesis son opcionales si ha sido predeclarada/importada.
&NOMBRE(LISTA); # Evita la definición dada por el prototipo.
&NOMBRE; # Hace el actual valor de @_ visible a la subrutina llamada.
Una subrutina puede ser llamada con un explícito prefijo &. El & es opcional en Perl modernos, así como los paréntesis si la subrutina ha sido predeclarada. El & no es opcional cuando sólo se está nombrando a la subrutina, como cuando se usa como un argumento a defined() y undef(). Ni tampoco cuando quieres hacer una llamada indirecta con el nombre de una subrutina o referencia usando las formas &$subref() o &{$subref}(), aunque la notación $subref->() soluciona el problema. Ver
perlref para más información sobre todo esto.
Veamos un ejemplo. Esta es la clásica solución de colocar primero el núcleo del programa y luego las subrutinas:
- Código: Seleccionar todo
1 #!/usr/bin/perl -l
2 use warnings;
3 use strict;
4
5 ### Constantes
6 my $k = 4;
7
8 ### Programa ###
9 for(my $i=1; $i<=10; $i++) {
10 print $i, " -> ", multiplica( $i );
11 }
12
13 ### subrutinas ###
14 sub multiplica {
15 my $arg = $_[0];
16 return $arg * $k;
17 }
(es un programa muy simple que saca la tabla de multiplicar de un valor $k)
En caso de tener un Perl antiguo, estariamos obligados a poner &multiplica($i) en la línea 10 porque la definición de la subrutina ocurre al final del código. En un Perl moderno, ya no es necesario, como en el ejemplo. En el caso contrario, en el de poner primero las subrutinas y luego el núcleo del programa, el poner el & sería opcional en cualquier Perl.
Ahora con prototipos. Vamos a declarar cómo van a ser las subrutinas antes de usarlas:
- Código: Seleccionar todo
1 #!/usr/bin/perl -l
2 use warnings;
3
4 sub mul($);
5
6 my $k = 4;
7
8 for(my $i=1; $i<=10; $i++) {
9 print $i, " -> ", mul $i, " ", $i+3;
10 }
11
12 sub mul ($) {
13 my $arg = $_[0];
14 return $arg * $k;
15 }
En la línea 4 declaramos que en nuestro programa existe una función llamada
mul que admite un argumento escalar (
$). La definición de la función está al final. Ves que en la lína 12 nos vemos obligados a poner los paréntesis con el prototipo. Perl busca una función tal cual la hemos declarado antes.
Y al llegar a la hora de usarla, en la línea 9, vemos que, opcionalmente, hemos podido quitar el & y los paréntesis. Al principio pensamos que Perl se puede liar cuando quitamos los paréntesis, ya que vemos que después de
mul $i sigue una coma y más elementos que podrían parecer más argumentos a la función mul (un espacio en blanco y la expresión
$i+3), pero gracias a la declaración de la línea 4, Perl ya sabe que
mul sólo tiene un argumento, el primer
$i.
Los prototipos sirven para la comprobación de los argumentos pasados a las subrutinas, en tiempo de compilación. Es una forma de obligarnos a nosotros mismos o a los usuarios de nuestras funciones a colocar de forma exacta los argumentos que esas subrutinas esperan.
Y finalmente, en el caso de poner primero las subrutinas antes del núcleo del programa, podemos ahorrarnos las declaraciones, prototipos, el & y los paréntesis.
La recomendación actual es usar los prototipos sólo para las nuevas funciones en los nuevos módulos que estemos construyendo, y no retocar las funciones de los módulos que ya hemos publicado. Pueden dar problemas a las personas que ya lo están usando.
Un ejemplo. Supongamos que tenemos este programa:
- Código: Seleccionar todo
#!/usr/bin/perl -lw
sub func {
my $n = shift;
print "Me has pasado un $n";
}
@foo = ("Hola");
func(@foo);
Tenemos una función sin prototipos. Al ejecutarlo, @foo, una lista, se manda a la función y ésta coge el primer valor y lo saca en pantalla, por lo que la salida del programa es:
Me has pasado un Hola. Pero, si cambiamos la línea
sub func { por otra con el prototipo
sub func ($) { indicando que sólo queremos un valor escalar, al llegar a la línea de llamar a la función func, @foo es interpretado en contexto escalar (eso es lo que espera func) por lo que entonces obtenemos el número de elementos que hay en @foo. La salida es ahora:
Me has pasado un 1.