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 }
$ 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.
$ 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 64Observe 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 |
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?