Paso de Listas y Hashes a Subrutinas

El aplanado al que somete Perl los arrays y hashes cuando son pasados a una subrutina hace que sean indistinguibles desde la perspectiva de la subrutina llamada. Una forma de evitar esta limitación consiste en pasar como argumentos las referencias a los arrays y los hashes implicados.

El siguiente ejemplo muestra una subrutina que permite realizar la suma, el producto, etc. de un número n de vectores. Para ello recibe una referencia a una subrutina implantando una operación binaria conmutativa y una lista de referencias a listas (una matriz en la que las filas pueden ser de distinta longitud). Por ejemplo, las siguientes llamadas son legales:

my @m  = operate( \&plus, \@b, \@a);
my @m2 = operate( \&times, \@a, \@b, \@c);
Donde las funciones plus y times vienen definidas por:
sub plus  {  $_[0]+$_[1] }
sub times {  $_[0]*$_[1] }

La siguiente ejecución ilustra el modo de funcionamiento:

lhp@nereida:~/Lperl/src$ vect3.pl
a = (3 2 1 9)
b = (5 6 7)
c = (1 2)
a+b:
8 8 8 9
a*b*c:
15 24 7 9
max(a, b, c):
5 6 7 9

La rutina operate devuelve un vector conteniendo el resultado de operar vectorialmente los diferentes vectores.

Si un vector es mas corto que otro se supone que es extendido a la longitud del mas largo con elementos iguales al elemento neutro de la operación usada. La operación se supone conmutativa:

lhp@nereida:~/Lperl/src$ cat -n vect3.pl
 1  #!/usr/bin/perl  -w
 2  use strict;
 3  use List::Util qw(reduce max);
 4
 5  sub operate {
 6    my $rop = shift;
 7    die "Provide a subref\n" unless ref($rop) eq 'CODE';
 8    my @length = map { scalar(@$_) } @_; # lengths of the different lists
 9
10    # index of longest list
11    our ($a, $b);
12    my $longest = reduce { $length[$a] < $length[$b]?$b:$a } 0..$#_;
13    my @res = @{splice @_, $longest, 1};
14
15    for my $v (@_) {
16      my $i = 0;
17      @res[0..$#$v] = map { $rop->($_, $res[$i++]) } @$v;
18    }
19    return @res;
20  }
21
22  sub plus  {  $_[0]+$_[1] }
23  sub times {  $_[0]*$_[1] }
24
25  my @a = (3, 2, 1, 9); print "a = (@a)\n";
26  my @b = (5, 6, 7);    print "b = (@b)\n";
27  my @c = (1, 2);       print "c = (@c)\n";
28  my @m = operate( \&plus, \@b, \@a);
29  print "a+b:\n@m\n";
30  my @m2 = operate( \&times, \@a, \@b, \@c);
31  print "a*b*c:\n@m2\n";
32  my @m3 = operate( \&max, \@a, \@b, \@c);
33  print "max(a, b, c):\n@m3\n";

La función reduce aplica el bloque de código a la lista. Inicialmente las variables $a y $b contienen los dos primeros elementos. El bloque es llamado y el resultado devuelto por el bloque queda en $a. A continuación $b recorre los elementos de la lista siendo operado con $a y dejando siempre el resultado en $a. El valor final de $a es el valor devuelto por reduce.

Casiano Rodríguez León
2009-10-04