Currying

Se denomina currying al proceso de representar una función de múltiples argumentos como una función de un sólo argumento. Por ejemplo, la función suma $x+$y es una función de dos argumentos ($x, $y), pero puede verse también como una función de un argumento $x. ¿Cómo?. La clave esta en pensar en la función suma como una función que cuando recibe el primer argumento $x devuelve como resultado una función sub { $x+$_[0] } la cual suma a su argumento el primer argumento $x. Para saber mas sobre currying consulte el libro Higher Order Perl [13]. El término currying hace alusión al lógico americano Haskell Curry (1900-1982) que popularizó el concepto en 1930. Al parecer Gottlob Frege ya había introducido la idea en 1893. Moses Schönfinkel la redescubrió en 1924.

Ejercicio 4.18.2   Describa en palabras los dominios inicial y final de las funciones:
  1. La función map.
  2. La función splice
  3. La función first: Consulte la documentación del módulo List::Util.

Una Función curry

El siguiente ejemplo muestra una función curry que recibe como argumento una función

$\displaystyle f : D_1 \times \ldots \times D_{n} \rightarrow E$    

y retorna una función que currifica esa función:

$\displaystyle curry(f) : D_1 \rightarrow \mathcal{F}(D_2 \times \ldots \times D_n, E)$    

donde $ \mathcal{F}(D_2 \times \ldots \times D_n, E)$ denota las funciones con argumentos en $ D_2 \times \ldots \times D_n$ y valores en $ E$.

Cuando a la función $ curry(f)$ se le pasa un argumento adicional $ x$ se obtiene una nueva función:

$\displaystyle curry(f)(x) : D_2 \times \ldots \times D_n \rightarrow E$    

Sigue un ejemplo de uso:

15  die "Error\n" unless @ARGV;
16  my $cprod = curry(sub { $_[0]*$_[1] });
17  my $doble = $cprod->(2);
18  my @dobles = map { $doble->($_) } @ARGV;
19  print "@dobles\n";
20
21  my $cfirst = curry(\&first);
22  my $fodd = $cfirst->( sub { $_ % 2 } ); # Primer impar
23  print $fodd->(@ARGV)."\n";
lhp@nereida:~/Lperl/src$ currying2.pl 4 8 12 9 23 25
8 16 24 18 46 50
9

Veamos el código y una ejecución:

lhp@nereida:~/Lperl/src$ cat -n currying2.pl
 1  #!/usr/bin/perl -w
 2  use strict;
 3  use List::Util qw(first);
 4
 5  sub curry {
 6    my $f = shift; # Referencia a una función de dos o mas argumentos
 7
 8    return sub {  # Retorna función como f pero con un arg. menos
 9      my $x = shift;
10      my $fx = sub { $f->($x, @_) };
11      return $fx;
12    };
13  }

La técnica de currificación transforma una función ordinaria en una fábrica de funciones (function factory). Al llamar a la fábrica con un parámetro nos devuelve una nueva función.

Ejercicio 4.18.3   ¿Que describe la expresión curry(\&List::Util::sum)->(4)->(1..5)? Si tiene dudas lea la siguiente sesión con el depurador:
lhp@nereida:~/Lperl/src$ perl -wd currying2.pl
main::(currying2.pl:15):        die "Error\n" unless @ARGV;
  DB<1>  $f = \&List::Util::sum
  DB<2> $g = curry($f)
  DB<3> $h = $g->(4)
  DB<4> x $h->(1..5)
0  19

Currificar map

Veamos otro ejemplo de currificación. La función map es una función de dos argumentos: el primero es código y el segundo una lista. La función retorna una lista. Matemáticamente:

$\displaystyle Map : \mathcal{F}(D, E) \times \mathcal{L}(D) \rightarrow \mathcal{L}(E)$    

donde $ \mathcal{F}(D, E)$ denota las funciones de $ D$ en $ E$ y $ \mathcal{L}(D)$ denota las listas de objetos de tipo $ D$.

Si la currificamos la convertiremos en una función de un sólo argumento que retorna funciones:

$\displaystyle Cmap : \mathcal{F}(D, E) \rightarrow \mathcal{F}(\mathcal{L}(D), \mathcal{L}(E))$    

Así pues $ Cmap(f)$ - para una función $ f$ dada - es una función que actúa sobre una lista y retorna una lista. Podríamos usarla como sigue:

lhp@nereida:~/Lperl/src/hop/Chap7$ sed -ne '11,$p' cmap2.pl | cat -n
 1  my $sizes  = cmap { -s $_ };
 2  my $rand20 = cmap { int(rand(20)) };
 3  local $a = 0;
 4  my $pref   = cmap { $_ += $a; $a = $_ };
 5
 6  my @s = $sizes->(@ARGV);
 7  my @r = $rand20->(1..5);
 8  my @p = $pref->(1..5);
 9
10  print "Tamaños = @s\n";
11  print "Aleatorio = @r\n";
12  print "Suma de prefijos = @p\n";
que al ejecutra resulta en:
lhp@nereida:~/Lperl/src/hop/Chap7$ cmap2.pl *.pm
Tamaños = 650 1933 591 1713
Aleatorio = 18 11 1 8 12
Suma de prefijos = 1 3 6 10 15
Veamos una implementación:
lhp@nereida:~/Lperl/src/hop/Chap7$ sed -ne '1,9p' cmap2.pl | cat -n
1  #!/usr/bin/perl -w
2  use strict;
3
4  sub cmap (&) {
5    my $f = shift;
6    return sub {
7      map { $f->($_) } @_
8    };
9  }



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