Paso de parámetros por valor y por referencia

En los ejemplos vistos hasta ahora, cuando invocamos una subrutina y le pasamos parámetros en realidad le estamos pasando una lista de datos, que se llama @_ . Cuando pasamos arreglos o tablas como parámetros en realidad los estamos vertiendo elemento por elemento en @_ , lo cual parece un poco estúpido a veces, con el trabajo que nos dio previamente llenarlos. De hecho, si queremos pasar por ejemplo dos arreglos a la vez, será imposible que la subrutina que los reciba sepa donde termina uno y empieza el otro, porque lo que está recibiendo es @_ .

Por esta razón, cuando pasamos varios parámetros y uno de ellos es un arreglo, debemos poner al arreglo en última posición, para poder identificarlos después en la subrutina. Lo mismo se aplica a los valores que devuelve una subrutina con return.

## forma correcta de pasar varios parametros y recibir mas de un resultado 

my ($result1,@result2) = subrutina($arg1,$arg2,@arg3);

sub subrutina
{
	my($sarg1,$sarg2,@sarg3) = @_; 

	my ($sresult1,@sresult2);

	return ($result1,@result2);
}

## en cambio, esto no funciona

my (@result2,$result1) = subrutina($arg1,@arg3,$arg3);  # $result1 queda sin asignar

sub subrutina
{
	my($sarg1,@sarg3,$sarg3) = @_;  # $sarg3 queda sin asignar

	my ($sresult1,@sresult2);

	return (@result2,$result1);
}

La forma más económica de pasar parámetros a subrutinas es pasarlos como referencias (ver sección 2.3). De esta manera no pasamos los datos en sí, si no la dirección que ocupan en memoria. Así no es necesario copiar todos los datos a @_ y la subrutina que reciba las referencias puede acceder a los datos en su organización original, ya sean escalares, arreglos o tablas asociativas.

Otra aplicación de las referencias parámetro es que permiten modificar de forma permanente, no local, los datos pasados como argumento. Finalmente, el paso por referencias permite pasar como parámetro otras subrutinas, lo cual puede ser útil en ocasiones.

Veamos un ejemplo:

#!/usr/bin/perl -w 
# Ejemplo escrito por Bruno Contreras

use strict; 

## variables del programa principal 
my ($escalar , @arreglo1 , @arreglo2);

## asignacion de valores
$escalar = 0;
@arreglo1 = (1,2,3);
@arreglo2 = (4,5,6);

## llamada a subrutina y conversion de la referencia recibida a un arreglo
my @resultado = @{ lee_arreglos( $escalar , \@arreglo1 , \@arreglo2 )  }; 

print "resultado: $resultado[0] , $resultado[1] , $resultado[2]\n";


############### subrutinas ##################

sub lee_arreglos
{
	my ($sarg1,$rarreglo1,$rarreglo2) = @_;

	# variables locales
	my @res = ( $sarg1, $rarreglo1->[0], $rarreglo2->[0] );
	
	return \@res;  # devuelve una referencia a un arreglo como resultado
}

Otro ejemplo es esta función muy útil para reordenar o barajar vectores in situ :

#!/usr/bin/perl -w 

use strict; 

my @vector = (1,2,3,4,5,6);

fisher_yates(\@vector);

foreach my $valor (@vector){ print "$valor\n" }

sub fisher_yates 
{
	# http://www.perlmonks.org/?node_id=199901

	my ($ref_baraja) = @_;  
	my ($cartai,$cartaj) = (scalar(@$ref_baraja),0); 
	
	while(--$cartai) 
	{ 
		$cartaj = int(rand($cartai+1)); 
		@$ref_baraja[$cartai,$cartaj] = @$ref_baraja[$cartaj,$cartai]; 
	}
}

Bruno Contreras-Moreira
http://www.eead.csic.es/compbio