sintaxis - Argumentos de la subrutina Perl
sintaxis de perl (7)
He estado leyendo acerca de Perl recientemente y estoy un poco perplejo sobre cómo Perl maneja los argumentos pasados a las subrutinas.
En un lenguaje como Python, Java o PHP, una definición de función toma la forma (en pseudocódigo):
function myFunc(arg1, arg2) {
// Do something with arg1 and arg2 here
}
Sin embargo, en Perl es solo:
sub mySub {
# @_ holds all arguments passed
}
Y tal como lo entiendo, esa es la única forma de hacerlo.
¿Qué pasa si quiero restringir que la persona que llama solo pase 2 argumentos?
¿No es esto solo que Perl no permite nada más que argumentos de número variable en otros idiomas (es decir, Python, C, etc.)?
¿No sería eso un problema en algún momento?
¿Qué pasa con la verificación de número de argumento predeterminada en otros idiomas? ¿Tendría que hacer eso explícitamente en Perl? Por ejemplo
sub a_sub { if (@_ == 2) { # Continue function } else { return false } }
Eres cauteloso con el entorno de Perl porque es bastante diferente de los idiomas que has encontrado antes.
Las personas que creen en prototipos de tipado y función fuertes estarán en desacuerdo aquí, pero creo que las restricciones de este tipo rara vez son útiles. ¿ Realmente le ha pillado C pasando el número incorrecto de parámetros a una función con la frecuencia suficiente para ser útil?
Es más común en Perl moderno copiar los contenidos de @_
a una lista de variables escalares léxicas, por lo que a menudo verá subrutinas comenzando con
sub mysub {
my ($p1, $p2) = @_;
... etc.
}
de esta forma, todos los parámetros que se pasen estarán disponibles como elementos de @_
( $_[0]
, $_[1]
etc.) mientras que los esperados son nombrados y aparecen en $p1
y $p2
(aunque espero que entiendo que esos nombres deben ser elegidos apropiadamente).
En el caso particular de que la subrutina sea un método , el primer parámetro es especial. En otros idiomas es self
o this
, pero en Perl es simplemente el primer parámetro en @_
y puede llamarlo como desee. En esas circunstancias, verías
sub method {
my $self = shift;
my ($p1, $p2) = @_;
... etc.
}
para que el objeto de contexto (o el nombre de la clase si es un método de clase) se extraiga en $self
(un nombre asumido por convención) y el resto de los parámetros permanezcan en @_
para acceder directamente o, más habitualmente , copiado a variables escalares locales como $p1
, $p2
etc.
Muy a menudo la queja es que tampoco hay verificación de tipo, por lo que puedo pasar cualquier escalar que quiera como parámetro de subrutina. Siempre que use strict
use warnings
use strict
y de use warnings
estén en contexto, incluso esto generalmente es fácil de depurar, simplemente porque las operaciones que la subrutina puede realizar en una forma de escalar son generalmente ilegales en otra.
Aunque originalmente estaba más relacionado con la encapsulación con respecto a Perl orientado a objetos, esta cita de Larry Wall es muy relevante
Perl no tiene una infatuación con la privacidad impuesta. Preferiría que te mantuvieras fuera de la sala porque no te invitaron, no porque tenga una escopeta
C fue diseñado e implementado en los días en que era un gran aumento de la eficiencia si se podía hacer que un programa defectuoso fallara durante la compilación en lugar de durante el tiempo de ejecución. Eso ha cambiado ahora, aunque ha surgido una situación similar con el JavaScript del lado del cliente, donde en realidad sería útil saber que el código es incorrecto antes de buscar los datos de Internet con los que debe lidiar. Lamentablemente, la comprobación de parámetros de JavaScript ahora es más flexible de lo que debería ser.
Actualizar
Para aquellos que dudan de la utilidad de Perl con fines didácticos, sugiero que es precisamente porque los mecanismos de Perl son tan simples y directos que son ideales para tales propósitos.
Cuando llama a una subrutina Perl, todos los parámetros de la llamada tienen alias en
@_
. Puede usarlos directamente para afectar los parámetros reales o copiarlos para evitar acciones externasSi llama a una subrutina Perl como un método, entonces el objeto o clase llamante se proporciona como el primer parámetro. De nuevo, la subrutina (método) puede hacer lo que quiera con
@_
Perl no administra su manejo de argumentos por usted. En cambio, proporciona una abstracción mínima y flexible y le permite escribir código que se ajuste a sus necesidades.
Pase por referencia
Por defecto, Perl pega un alias a cada argumento en @_
. Esto implementa una semántica básica, pasa por referencia .
my $num = 1;
foo($num);
print "$num/n"; # prints 2.
sub foo { $_[0]++ }
Pasar por referencia es rápido pero tiene el riesgo de filtrar cambios en los datos de los parámetros.
Pase por copia
Si desea pasar por la semántica de copia , necesita hacer las copias usted mismo. Dos enfoques principales para manejar listas de parámetros posicionales son comunes en la comunidad de Perl:
sub shifty {
my $foo = shift;
}
sub listy {
my ($foo) = @_;
}
En mi lugar de trabajo hacemos una versión de Listy:
sub fancy_listy {
my ($positional, $args, @bad) = @_;
die "Extra args" if @bad;
}
Parámetros nombrados
Otra práctica común es el uso de parámetros con nombre :
sub named_params {
my %opt = @_;
}
Algunas personas están contentas con lo anterior. Prefiero un enfoque más detallado:
sub named_params {
my %opt = @_;
my $named = delete $opt{named} // "default value";
my $param = delete $opt{param}
or croak "Missing required ''param''";
croak "Unknown params:", join ", ", keys %opt
if %opt;
# do stuff
}
Esto descomprime parámetros nombrados en variables, permite espacio para la validación básica y los valores predeterminados y exige que no se pasen argumentos extra desconocidos.
En prototipos Perl
Los "prototipos" de Perl no son prototipos en el sentido normal. Solo proporcionan sugerencias de compilación que le permiten omitir los paréntesis en las llamadas a funciones. El único uso razonable es imitar el comportamiento de las funciones incorporadas. Puede vencer fácilmente la comprobación de prototipos de argumentos. En general, NO USE PROTOTIPOS . Úselos con cuidado para que use la sobrecarga del operador, es decir, con moderación y solo para mejorar la legibilidad.
Perl tiene la capacidad de prototyping para marcadores de posición de parámetros, que está acostumbrado a ver, pero a menudo es innecesario.
sub foo($){
say shift;
};
foo(); # Error: Not enough arguments for main::foo
foo(''bar''); # executes correctly
Y si sub foo($$){...}
requeriría 2 argumentos no opcionales (por ejemplo, foo(''bar'',''baz'')
)
Por alguna razón, a Perl le gustan las listas y no le gusta la tipificación estática. La matriz @_
realmente abre mucha flexibilidad, porque los argumentos de la subrutina se pasan por referencia , y no por valor . Por ejemplo, esto nos permite hacer argumentos externos:
my $x = 40;
add_to($x, 2);
print "$x/n"; # 42
sub add_to { $_[0] += $_[1] }
... pero esto es más un hack de rendimiento histórico. Por lo general, los argumentos son "declarados" por una asignación de lista:
sub some_sub {
my ($foo, $bar) = @_;
# ^-- this assignment performs a copy
...
}
Esto hace que la semántica de esta sub llamada por valor, que generalmente es más deseable. Sí, los argumentos no utilizados simplemente se olvidan, y muy pocos argumentos no undef
ningún error automático: las variables solo contienen undef
. Puede agregar validación arbitraria, por ejemplo, verificando el tamaño de @_
.
Existen planes para finalmente hacer que los parámetros con nombre estén disponibles en el futuro, lo que se vería como
sub some_sub($foo, $bar) { ... }
Puede tener esta sintaxis hoy si instala el módulo de signatures
. Pero hay algo aún mejor: recomiendo encarecidamente Function::Parameters
, que permite una sintaxis como
fun some_sub($foo, $bar = "default value") { ... }
method some_method($foo, $bar, :$named_parameter, :$named_with_default = 42) {
# $self is autodeclared in methods
}
Esto también es compatible con las comprobaciones de tipo experimental.
¡Extensiones del analizador FTW!
Puedes simplemente usar:
my ($arg1, $arg2) = @_;
Para limitar explícitamente la cantidad de argumentos que puede usar:
my $number =2;
die "Too many arguments" if @_ > $number;
Si está leyendo sobre Perl recientemente, lea perl reciente. También puede leer el libro Modern Perl de forma gratuita: libro gratuito en línea Modern Perl
Aquí hay algunos ejemplos de ese libro sobre firmas de funciones:
sub greet_one($name = ''Bruce'') {
say "Hello, $name!";
}
sub greet_all($leader, @everyone) {
say "Hello, $leader!";
say "Hi also, $_." for @everyone;
}
sub make_nested_hash($name, %pairs) {
return { $name => /%pairs };
}
Si realmente desea imponer controles de parámetros más estrictos en Perl, podría ver algo como Params :: Validate .