Es malo 'cambio' para el procesamiento de parámetros de subrutinas de Perl?
-
19-09-2019 - |
Pregunta
Estoy usando frecuentemente shift
para desempaquetar los parámetros de función:
sub my_sub {
my $self = shift;
my $params = shift;
....
}
Sin embargo, muchos de mis colegas están predicando que shift
es en realidad el mal. Podría explicar por qué yo preferiría
sub my_sub {
my ($self, $params) = @_;
....
}
para shift
?
Solución
El uso de shift
para desempaquetar argumentos no es malo. Es una convención común y puede ser la manera más rápida para procesar argumentos (dependiendo de cuántos son y cómo se transmiten). Aquí está un ejemplo de un escenario poco común en el que ese es el caso:. Un descriptor de acceso sencilla
use Benchmark qw(cmpthese);
sub Foo::x_shift { shift->{'a'} }
sub Foo::x_ref { $_[0]->{'a'} }
sub Foo::x_copy { my $s = $_[0]; $s->{'a'} }
our $o = bless {a => 123}, 'Foo';
cmpthese(-2, { x_shift => sub { $o->x_shift },
x_ref => sub { $o->x_ref },
x_copy => sub { $o->x_copy }, });
Los resultados en Perl 5.8.8 en mi máquina:
Rate x_copy x_ref x_shift
x_copy 772761/s -- -12% -19%
x_ref 877709/s 14% -- -8%
x_shift 949792/s 23% 8% --
No es espectacular, pero es así. Siempre pruebe su escenario de la versión de Perl en su hardware de destino para salir de dudas.
shift
también es útil en los casos en que desea desplazar fuera de las invocaciones y luego llamar a un método SUPER::
, pasando el @_
restante tal y como son.
sub my_method
{
my $self = shift;
...
return $self->SUPER::my_method(@_);
}
Si tuviera una larga serie de operaciones my $foo = shift;
en la parte superior de una función, sin embargo, podría considerar el uso de una copia masiva de @_
lugar. Pero, en general, si tiene una función o un método que lleva más de un puñado de argumentos, utilizando parámetros con nombre (es decir, la captura de todos @_
en un hash %args
o esperando un solo argumento referencia a un hash) es un enfoque mucho mejor.
Otros consejos
No es malo, es una especie gusto de cosas. Es frecuente encontrar que los estilos utilizados en conjunto:
sub new {
my $class = shift;
my %self = @_;
return bless \%self, $class;
}
Yo tiendo a usar shift
cuando hay un argumento o cuando quiero tratar a los primeros argumentos de manera diferente que el resto.
Esto es, como han dicho otros, es cuestión de gustos.
generalmente prefiero a cambiar mis parámetros en lexicals porque me da un lugar estándar para declarar un conjunto de variables que serán utilizados en la subrutina. El nivel de detalle adicional me da un lugar agradable para pasar los comentarios. También hace que sea fácil de proporcionar valores predeterminados de una manera ordenada.
sub foo {
my $foo = shift; # a thing of some sort.
my $bar = shift; # a horse of a different color.
my $baz = shift || 23; # a pale horse.
# blah
}
Si está realmente preocupado por la velocidad de llamar a sus rutinas, no desempacar sus argumentos en absoluto - acceder a ellos directamente a través de @_
. Tenga cuidado, esas son las referencias a los datos de la persona que llama que se está trabajando. Se trata de un lenguaje común en el POE. POE ofrece un montón de constantes que se utilizan para obtener parámetros de posición por su nombre:
sub some_poe_state_handler {
$_[HEAP]{some_data} = 'chicken';
$_[KERNEL]->yield('next_state');
}
Ahora la gran error estúpido que puede obtener si usted habitualmente desembalar params con cambio es la siguiente:
sub foo {
my $foo = shift;
my $bar = shift;
my @baz = shift;
# I should really stop coding and go to bed. I am making dumb errors.
}
Creo que el estilo de código consistente es más importante que cualquier estilo particular. Si todos mis compañeros de trabajo utilizado el estilo de lista de asignación, que haría uso de ella también.
Si mis compañeros de trabajo dijeron que había un gran problema usando turno para descomprimir, me gustaría pedir una demostración de por qué es malo. Si el caso es sólido, entonces yo iba a aprender algo. Si el caso es falsa, podría entonces refuto y ayudar a detener la propagación de la anti-conocimiento. A continuación, me gustaría sugerir que determinamos un método estándar y siga durante futuro código. Incluso podría tratar de establecer una política de Perl :: crítico para comprobar la decidida estándar.
Asignación de @_ a una lista puede traer algunas características complementarias que very helpul.
Esto hace que sea un poco más fácil para añadir parámetros con nombre adicionales en una fecha posterior, al modificar su código Algunas personas consideran esto una característica, similar a la forma de terminar una lista o un hash con un terminador '' hace que sea un poco más fácil anexar miembros en el futuro.
Si estás en el hábito de usar este idioma, entonces el desplazamiento de los argumentos que podría parecer perjudicial, ya que si se edita el código para añadir un argumento adicional, que podría terminar con un error sutil, si no paga atención.
por ejemplo.
sub do_something {
my ($foo) = @_;
}
editado más tarde a
sub do_something {
my ($foo,$bar) = @_; # still works
}
Sin embargo
sub do_another_thing {
my $foo = shift;
}
Si otro colega, que utiliza la primera forma dogmática (tal vez piensan cambio es malo) edita su archivo y actualizaciones distraídamente esto para leer
sub do_another_thing {
my ($foo, $bar) = shift; # still 'works', but $bar not defined
}
y que pueden haber introducido un error sutil.
Asignar a @_ pueden ser más compacto y eficiente con el espacio vertical, cuando se tiene un pequeño número de parámetros para asignar a la vez. También permite que se les suministre los argumentos por defecto si usted está utilizando el estilo de hash de los parámetros de función llamada
por ejemplo.
my (%arguments) = (user=>'defaultuser',password=>'password',@_);
Me siguen considerando que es una cuestión de estilo / gusto. Creo que lo más importante es aplicar un estilo u otro con la consistencia, obedeciendo al principio de la menor sorpresa.
No creo shift
es malo. El uso de shift
muestra su disposición a nombrar las variables realidad -. En lugar de utilizar $_[0]
En lo personal, yo uso shift
cuando sólo hay un parámetro a una función. Si tengo más de un parámetro, voy a utilizar el contexto de la lista.
my $result = decode($theString);
sub decode {
my $string = shift;
...
}
my $otherResult = encode($otherString, $format);
sub encode {
my ($string,$format) = @_;
...
}
Perl :: crítico es su amigo. Sigue las las "normas" establecidas en el libro de Damian Conway Perl Best Practices. Correr con --verbose 11 le da una explicación sobre por qué las cosas están mal. No desembalaje @_ por primera vez en sus submarinos es una severidad 4 (de 5). Por ejemplo:
echo 'package foo; use warnings; use strict; sub quux { my foo= shift; my (bar,baz) = @_;};1;' | perlcritic -4 --verbose 11
Always unpack @_ first at line 1, near 'sub quux { my foo= shift; my (bar,baz) = @_;}'.
Subroutines::RequireArgUnpacking (Severity: 4)
Subroutines that use `@_' directly instead of unpacking the arguments to
local variables first have two major problems. First, they are very hard
to read. If you're going to refer to your variables by number instead of
by name, you may as well be writing assembler code! Second, `@_'
contains aliases to the original variables! If you modify the contents
of a `@_' entry, then you are modifying the variable outside of your
subroutine. For example:
sub print_local_var_plus_one {
my ($var) = @_;
print ++$var;
}
sub print_var_plus_one {
print ++$_[0];
}
my $x = 2;
print_local_var_plus_one($x); # prints "3", $x is still 2
print_var_plus_one($x); # prints "3", $x is now 3 !
print $x; # prints "3"
This is spooky action-at-a-distance and is very hard to debug if it's
not intentional and well-documented (like `chop' or `chomp').
An exception is made for the usual delegation idiom
`$object->SUPER::something( @_ )'. Only `SUPER::' and `NEXT::' are
recognized (though this is configurable) and the argument list for the
delegate must consist only of `( @_ )'.
No es una optimización para la asignación de la lista.
La única referencia que pude encontrar, es éste .
5.10.0
desactivado inadvertidamente una optimización, lo que provocó una caída de rendimiento medible en la lista la asignación, como a menudo se utiliza para asignar parámetros de la función de@_
. La optimización se ha reinstalado, y la regresión de rendimiento fijo.
Este es un ejemplo de la regresión rendimiento afectada.
sub example{
my($arg1,$arg2,$arg3) = @_;
}
No es intrínsecamente mala, pero usarlo de lograr los argumentos de una subrutina de uno en uno es comparativamente lento y requiere un mayor número de líneas de código.