Domanda

Sto usando frequentemente shift per decomprimere i parametri di funzione:

sub my_sub {
    my $self = shift;
    my $params = shift;
    ....
}

Tuttavia, molti miei colleghi che predicano shift è in realtà il male. Ci può spiegare il motivo per cui dovrei preferire

sub my_sub {
    my ($self, $params) = @_;
    ....
}

per shift?

È stato utile?

Soluzione

L'uso di shift per disfare argomenti non è male. Si tratta di una convenzione comune e può essere il modo più veloce per elaborare gli argomenti (a seconda di quanti ci sono e come sono passati). Ecco un esempio di uno scenario alquanto comune in cui questo è il caso:. Una semplice funzione di accesso

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  }, });

I risultati su Perl 5.8.8 sulla mia macchina:

            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%      --

Non è drammatico, ma è così. Verificare sempre lo scenario della versione di perl sul vostro hardware di destinazione per scoprire di sicuro.

shift è utile anche nei casi in cui si desidera spostare fuori dal invocant e quindi chiamare un metodo SUPER::, passando il restante @_ così com'è.

sub my_method
{
  my $self = shift;
  ...
  return $self->SUPER::my_method(@_);
}

Se ho avuto una lunga serie di operazioni my $foo = shift; nella parte superiore di una funzione, però, potrei considerare l'utilizzo di una copia di massa da @_ invece. Ma in generale, se si dispone di una funzione o un metodo che richiede più di una manciata di argomenti, utilizzando parametri denominati (ad esempio, la cattura di tutti @_ in un hash %args o in attesa di un singolo argomento di riferimento hash) è un approccio molto migliore.

Altri suggerimenti

Non è male, è un assaggio genere di cose. Vedrete spesso gli stili utilizzati insieme:

sub new {
    my $class = shift;
    my %self = @_;
    return bless \%self, $class;
}

Io tendo ad usare shift quando c'è un argomento o quando voglio trattare i primi argomenti in modo diverso rispetto al resto.

Questa è, come altri hanno detto, una questione di gusto.

In generale, preferisco di spostare i miei parametri in lessicali perché mi dà un posto standard per dichiarare un gruppo di variabili che verranno utilizzate nella subroutine. Il livello di dettaglio in più mi dà un bel posto dove rilassarsi commenti. Inoltre lo rende facile fornire valori di default in modo ordinato.

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
}

Se siete veramente preoccupati per la velocità di chiamare la vostra routine, non disfare le argomentazioni a tutti - accedervi direttamente utilizzando @_. Attenzione, questi sono i riferimenti ai dati del chiamante si sta lavorando. Si tratta di un linguaggio comune in POE. POE prevede una serie di costanti che si utilizza per ottenere i parametri posizionali per nome:

sub some_poe_state_handler {
    $_[HEAP]{some_data} = 'chicken';
    $_[KERNEL]->yield('next_state');
}

Ora la grande stupido bug si può ottenere se si apre la confezione abitualmente params con spostamento è questa:

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.

}

Credo che lo stile di codice coerente è più importante di qualsiasi stile particolare. Se tutti i miei colleghi hanno utilizzato lo stile lista di attribuzione, userei troppo.

Se i miei colleghi ha detto che v'è stato un grosso problema con spostamento a disfare, mi piacerebbe chiedere una dimostrazione del perché è male. Se il caso è solido, allora avrei imparato qualcosa. Se il caso è falsa, allora potrei confuto e aiutare a fermare la diffusione di anti-conoscenza. Quindi suggerirei che determiniamo un metodo standard e proseguire per il codice futuro. Potrei anche provare a impostare una politica di Perl :: Critic per verificare la decisa standard.

L'assegnazione @_ ad una lista può portare alcune caratteristiche helpul addtional.

Si rende leggermente più facile per aggiungere ulteriori parametri con nome in un secondo momento, come si modifica il codice Alcune persone considerano questa una caratteristica, simile a come finire una lista o un hash con una '' rende un po 'più facile accodare membri in futuro.

Se siete in l'abitudine di usare questo idioma, quindi spostando gli argomenti potrebbe sembrare pericoloso, perché se si modifica il codice per aggiungere un argomento in più, si potrebbe finire con una sottile bug, se non si paga attenzione.

per es.

sub do_something {
 my ($foo) = @_;
}

in seguito a cura di

sub do_something {
  my ($foo,$bar) = @_; # still works
}

ma

sub do_another_thing {
  my $foo = shift;
}

Se un altro collega, che utilizza la prima forma dogmaticamente (forse pensano spostamento è male) modifica il file e gli aggiornamenti distrattamente questo per leggere

sub do_another_thing {
  my ($foo, $bar)  = shift; # still 'works', but $bar not defined
}

e che possono avere introdotto un bug sottile.

L'assegnazione a @_ può essere più compatto ed efficiente con lo spazio in verticale, quando si dispone di un piccolo numero di parametri da assegnare in una volta. Permette anche per voi di fornire argomenti di default se si sta utilizzando lo stile hash di parametri di funzione di nome

per es.

 my (%arguments) = (user=>'defaultuser',password=>'password',@_);

Vorrei ancora considerare una questione di stile / gusto. Penso che la cosa più importante è quello di applicare uno stile o l'altro con coerenza, obbedendo al principio della minima sorpresa.

Non credo shift è il male. L'uso di shift mostra la volontà di nominare in realtà variabili -. Invece di usare $_[0]

Personalmente, io uso shift quando c'è solo un parametro a una funzione. Se ho più di un parametro, userò il contesto di lista.

 my $result = decode($theString);

 sub decode {
   my $string = shift;

   ...
 }

 my $otherResult = encode($otherString, $format);

 sub encode {
   my ($string,$format) = @_;
   ...
 }

Perl :: Critic è tuo amico qui. Essa segue le "norme" istituite nel libro di Damian Conway Perl Best Practices. Esecuzione con --verbose 11 si dà una spiegazione sul perché le cose vanno male. Non disimballaggio @_ prima nei vostri subs è una severità 4 (su 5). Per esempio:

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 `( @_ )'.

C'è un'ottimizzazione per l'assegnazione lista.

L'unico riferimento che ho trovato, è questo .

  

5.10.0 inavvertitamente disattivata un'ottimizzazione, che ha causato una   calo di prestazioni misurabili in lista   assegnazione, come è spesso usato per   assegnare i parametri funzionali da @_.   L'ottimizzazione è stato ri-insediato,   e la regressione prestazioni fisso.

Questo è un esempio della regressione prestazioni interessata.

sub example{
  my($arg1,$arg2,$arg3) = @_;
}

Non è intrinsecamente cattivo, ma usando per tirare fuori gli argomenti di una subroutine uno ad uno è relativamente lento e richiede un numero maggiore di linee di codice.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top