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?

¿Fue útil?

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.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top