next up previous contents index practicapracticaPP2moodleLHPmoodlepserratacpanmodulospauseperlgoogleetsiiullpcgull
Sig: Clausuras Sup: Referencias Ant: Referencias simbólicas y typeglobs Err: Si hallas una errata ...


Prototipos

Los prototipos en Perl son un mecanismo que permite la activación de ciertas comprobaciones asi como escribir subrutinas cuyo comportamiento disfrute de privilegios similares a los de los operadores internos de Perl.

Como ejemplo, supongamos que queremos escribir una función shift2 que elimina los dos primeros elementos del principio de un array. Supongamos que queremos usarla como shift:

@a = 1..10;
($f, $s) = shift2 @a;

Si queremos escribirla, no vale hacer:

sub shift2 { splice @_, 0, 2 }

Ejercicio 5.15.1   Observe el comportamiento del siguiente programa.
$ cat -n shift2.pl
     1  #!/usr/bin/perl -w
     2  use strict;
     3
     4  sub shift2 { splice @_, 0, 2 }
     5
     6  my @a = 1..5;
     7  my ($f, $g) = shift2 @a;
     8
     9  local $" = ',';
    10  print "f = $f, g = $g  \@a = (@a)\n";
$ ./shift2.pl
f = 1, g = 2  @a = (1,2,3,4,5)
¿Sabria explicar la salida?

La solución al problema es pasar el array por referencia:

sub shift2 { splice @{$_[0]}, 0, 2 }
pero entonces la llamada queda distinta, obligándonos a pasar la referencia:

@a = 1..10;
($f, $s) = shift2 \@a;

Si queremos que la interfase de shift2 emule el comportamiento de unshift podemos lograrlo, haciendo uso de los prototipos. Podemos redeclarar la función como:

sub shift2(\@) { splice @{$_[0]}, 0, 2 }

Aqui el prototipo \@ indica que si la rutina es llamada con un argumento de tipo array, el argumento será automáticamente convertido a una referencia a un array. Asi las llamadas siguientes producen error:

@x = shift2 %a
Type of arg 1 to main::shift2 must be array (not hash dereference) 

@x = shift2 \@a
Type of arg 1 to main::shift2 must be array

@x = shift2 @a, @b
Too many arguments for main::shift2

Como se ve en el ejemplo, se comprueba que el número y la clase de los parametros coinciden. Para que los prototipos trabajen de esta forma la llamada a la función debe hacerse sin prefijarse de &. Vea el siguiente ejemplo:

$ cat -n ./shift2.pl
 1  #!/usr/bin/perl -w
 2  use strict;
 3
 4  sub shift2(\@@) { my $arr = shift; splice @$arr, 0, 2 }
 5
 6  my @a = 1..6;
 7  my ($f, $g) = shift2 @a;
 8
 9  local $" = ',';
10  print "f = $f, g = $g  \@a = (@a)\n";
11
12  ($f, $g) = shift2(@a);
13  print "f = $f, g = $g  \@a = (@a)\n";
14
15  # dará error
16  ($f, $g) = &shift2(@a);
17  print "f = $f, g = $g  \@a = (@a)\n";
$ ./shift2.pl
f = 1, g = 2  @a = (3,4,5,6)
f = 3, g = 4  @a = (5,6)
Can't use string ("5") as an ARRAY ref while "strict refs" in use at ./shift2.pl line 4.
La llamada de la línea 16 produce un error: lo queda de @a es enviado como parámetro a la rutina y se produce la protesta.

Los prototipos se construyen a partir de átomos prototipo los cuáles estan hechos de un prefijo o de un escape y un prefijo. Así un prototipo como \$ le indica a Perl que debe pasar a la subrutina la referencia al argumento escalar.

Sin embargo un prototipo de la forma $ lo que hace es que fuerza un contexto escalar.

Ejercicio 5.15.2   Observe la conducta del siguiente programa:
$ cat -n ./dollarproto.pl
     1  #!/usr/bin/perl -w
     2  use strict;
     3
     4  sub t ($@) { my $a = shift; my @b = @_; print "a = $a, b = (@b)\n"; }
     5
     6  my ($a, $b, $c) = qw/uno dos tres/;
     7  t ':',$a, $b, $c;
     8
     9  my @r = 1..5;
    10  t @r;
    11
$ ./dollarproto.pl
a = :, b = (uno dos tres)
a = 5, b = ()
¿Podría explicar la salida?

Sin embargo, un prototipo del tipo @ o del tipo % no sólo fuerza un contexto de lista sino que se traga todo el resto de argumentos.

Un prototipo de la forma & fuerza una referencia a código. Si se trata del primer argumento, entonces la palabra sub es opcional y se puede llamar como los operadores eval o grep:

$ cat -n ampproto.pl
     1  #!/usr/bin/perl -w
     2  use strict;
     3
     4  sub t (&$) { my ($f, $x) = @_; print &$f($x),"\n" }
     5
     6  t { $_[0]**3 } 4;
     7
$ ./ampproto.pl
64
Observe la ausencia de coma en la llamada de la línea 6.

Si el prototipo es * fuerza una referencia a un typeglob. Por último un prototipo de la forma ; separa los argumentos requeridos de los argumentos opcionales.

Veamos otros ejemplos de declaraciones de prototipos de operadores ya existentes en Perl:



myreverse (@) myreverse $a,$b,$c
myjoin ($@) myjoin ":",$a,$b,$c
mypop (\@) mypop @array
mysplice (\@$$@) mysplice @array,@array,0,@pushme
mykeys (\%) mykeys %{$hashref}
myopen (*;$) myopen HANDLE, $name
mypipe (**) mypipe READHANDLE, WRITEHANDLE
mygrep (&@) mygrep { /foo/ } $a,$b,$c
myrand ($) myrand 42
mytime () mytime

Ejercicio 5.15.3   Suponga que quiere escribir una función mypush que actúa exactamente como lo hace push: La llamada push @a, 4, 5, 6 empuja los elementos de la lista (4, 5, 6) en @a. ¿Como debería ser el prototipado de dicha subrutina?


next up previous contents index practicapracticaPP2moodleLHPmoodlepserratacpanmodulospauseperlgoogleetsiiullpcgull
Sig: Clausuras Sup: Referencias Ant: Referencias simbólicas y typeglobs Err: Si hallas una errata ...
Casiano Rodríguez León
2006-02-21