È il male 'shift' per elaborare i parametri di subroutine Perl?
-
19-09-2019 - |
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
?
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.