Gibt es einen Unterschied zwischen Perl-Verschiebung im Vergleich zu Zuordnung von @_ für Unterprogramm-Parameter?
-
03-07-2019 - |
Frage
Lassen Sie uns für einen Moment ignorieren.
Gibt es einen Unterschied zwischen den beiden folgenden Beispielen in Bezug auf die Leistung oder Funktionalität?
Mit shift
:
sub do_something_fantastical {
my $foo = shift;
my $bar = shift;
my $baz = shift;
my $qux = shift;
my $quux = shift;
my $corge = shift;
}
Mit @_
:
sub do_something_fantastical {
my ($foo, $bar, $baz, $qux, $quux, $corge) = @_;
}
Vorausgesetzt, dass beide Beispiele die gleiche in Bezug auf Leistung und Funktionalität, was denken die Leute über ein Format über den anderen? Offensichtlich ist das Beispiel @_
Verwendung ist weniger Codezeilen, aber ist es nicht mehr lesbar shift
zu verwenden, wie in dem anderen Beispiel gezeigt? Meinungen mit guter Argumentation sind willkommen.
Lösung
Es gibt einen funktionalen Unterschied. Die verschieben ändert @_
und die Zuordnung von @_
nicht. Wenn Sie brauchen, um später nicht verwenden @_
, dass Unterschied wahrscheinlich Sie spielt keine Rolle. Ich versuche immer, die Listenzuordnung zu verwenden, aber ich manchmal shift
.
Wenn ich jedoch mit shift
beginnen, etwa so:
my( $param ) = shift;
ich diese Fehler häufig erstellen:
my( $param, $other_param ) = shift;
Das ist, weil ich shift
nicht verwenden, die oft, so dass ich vergessen, auf die rechte Seite der Zuweisung verwinden, dass zu @_
zu ändern. Das ist der Punkt der besten Praxis in nicht shift
verwenden. Ich konnte getrennte Leitungen für jeden shift
machen, wie Sie in Ihrem Beispiel tun, aber das ist nur langweilig.
Andere Tipps
Mindestens auf meinen Systemen, wie es scheint, auf die Version von Perl und Architektur abhängen:
#!/usr/bin/perl -w
use strict;
use warnings;
use autodie;
use Benchmark qw( cmpthese );
print "Using Perl $] under $^O\n\n";
cmpthese(
-1,
{
shifted => 'call( \&shifted )',
list_copy => 'call( \&list_copy )',
}
);
sub call {
$_[0]->(1..6); # Call our sub with six dummy args.
}
sub shifted {
my $foo = shift;
my $bar = shift;
my $baz = shift;
my $qux = shift;
my $quux = shift;
my $corge = shift;
return;
}
sub list_copy {
my ($foo, $bar, $baz, $qux, $quux, $corge) = @_;
return;
}
Ergebnisse:
Using Perl 5.008008 under cygwin
Rate shifted list_copy
shifted 492062/s -- -10%
list_copy 547589/s 11% --
Using Perl 5.010000 under MSWin32
Rate list_copy shifted
list_copy 416767/s -- -5%
shifted 436906/s 5% --
Using Perl 5.008008 under MSWin32
Rate shifted list_copy
shifted 456435/s -- -19%
list_copy 563106/s 23% --
Using Perl 5.008008 under linux
Rate shifted list_copy
shifted 330830/s -- -17%
list_copy 398222/s 20% --
So wie es aussieht list_copy in der Regel 20% schneller ist als Verschiebung, außer unter Perl 5.10, wo Verschiebung tatsächlich etwas schneller!
Beachten Sie, dass diese schnell abgeleitete Ergebnisse waren. Die tatsächliche Geschwindigkeit Unterschiede werden größer als das, was hier aufgeführt ist, da Benchmark auch die Zeit zählt genommen zu rufen und die Subroutinen zurückkehren, die eine dämpfende Wirkung auf die Ergebnisse haben. Ich habe keine Untersuchung durchgeführt, um zu sehen, ob Perl eine spezielle Art von Optimierung tut. Ihre Ergebnisse können variieren.
Paul
Ich könnte mir vorstellen, die Verschiebung Beispiel ist langsamer als die Verwendung von @_ weil es 6 Funktionsaufrufe statt 1. Unabhängig davon, ob es auffällig oder sogar messbar ist eine andere Frage. Werfen Sie jeweils in einer Schleife von 10k Iterationen und Zeit sie.
Wie für Ästhetik, ziehe ich die @_ Methode. Es scheint, wie es zu einfach zu versauen die Reihenfolge der Variablen sein würde, die Shift-Methode mit einer zufälligen Ausschneiden und Einfügen verwenden. Außerdem habe ich so etwas wie dies viele Leute gesehen:
sub do_something {
my $foo = shift;
$foo .= ".1";
my $baz = shift;
$baz .= ".bak";
my $bar = shift;
$bar .= ".a";
}
Das, IMHO, sehr böse ist und leicht zu Fehlern führen könnte, zum Beispiel wenn Sie schneiden den baz Block und fügen Sie diesen unter dem Profilblock. Ich bin für die Definition von Variablen in der Nähe, wo sie verwendet werden, aber ich denke, die übergebenen Variablen am Anfang der Funktion definiert, hat Vorrang.
Normalerweise verwende ich die erste Version. Das ist, weil ich in der Regel Fehler haben müssen zusammen mit den Verschiebungen überprüft, die leichter zu schreiben ist. Sprich:
sub do_something_fantastical {
my $foo = shift || die("must have foo");
my $bar = shift || 0; # $bar is optional
# ...
}
Ich ziehe es als Liste (Ihr zweites Beispiel) entpacken @_. Obwohl, wie alles in Perl gibt es Fälle, in denen Verschiebung mit nützlich sein kann. Zum Beispiel PASSTHRU Methoden, die in einer Basisklasse außer Kraft gesetzt werden sollen, aber Sie wollen sicherstellen, dass die Dinge noch arbeiten, wenn sie nicht außer Kraft gesetzt werden.
package My::Base;
use Moose;
sub override_me { shift; return @_; }
Ich ziehe es mit
sub do_something_fantastical {
my ( $foo, $bar, $baz, $qux, $quux, $corge ) = @_;
}
Weil es besser lesbar ist. Wenn dieser Code nicht zu viel oft genannt wird, ist es wert Art und Weise. In sehr seltenen Fällen möchten Sie Funktion oft und als verwenden @_ direkt aufgerufen werden. Es ist effektiv, nur für sehr kurze Funktionen und Sie müssen sicher sein, dass diese Funktion in Zukunft nicht (Write Once-Funktion) entwickeln wird. Ich diesem Fall habe ich in 5.8.8 gebenchmarkt, die für die einzelnen Parameter verschieben schneller als $ _ [0], aber für zwei Parameter mit $ _ [0] und $ _ [1] ist schneller als verschiebung, verschieben.
sub fast1 { shift->call(@_) }
sub fast2 { $_[0]->call("a", $_[1]) }
Aber zurück zu Ihrer Frage. Ich habe auch lieber @_ Zuordnung in einer Reihe über Verschiebungen für viele Parameter auf diese Weise
sub do_something_fantastical2 {
my ( $foo, $bar, $baz, @rest ) = @_;
...
}
Wenn Suspect @rest nicht zu viel groß sein wird. In einem anderen Fall
sub raise {
my $inc = shift;
map {$_ + $inc} @_;
}
sub moreSpecial {
my ($inc, $power) = (shift(), shift());
map {($_ + $inc) ** $power} @_;
}
sub quadratic {
my ($a, $b, $c) = splice @_, 0, 3;
map {$a*$_*$_ + $b*$_ + $c} @_;
}
In selten Fällen I Endrekursion Optimierung müssen (von Hand natürlich) dann muss ich arbeiten direkt mit @_, als für kurze Funktion wert ist.
sub _switch #(type, treeNode, transform[, params, ...])
{
my $type = shift;
my ( $treeNode, $transform ) = @_;
unless ( defined $type ) {
require Data::Dumper;
die "Broken node: " . Data::Dumper->Dump( $treeNode, ['treeNode'] );
}
goto &{ $transform->{$type} } if exists $transform->{$type};
goto &{ $transform->{unknown} } if exists $transform->{unknown};
die "Unknown type $type";
}
sub switchExTree #(treeNode, transform[, params, ...])
{
my $treeNode = $_[0];
unshift @_, $treeNode->{type}; # set type
goto &_switch; # tail call
}
sub switchCompact #(treeNode, transform[, params, ...])
{
my $treeNode = $_[0];
unshift @_, (%$treeNode)[0]; # set type given as first key
goto &_switch; # tail call
}
sub ExTreeToCompact {
my $tree = shift;
return switchExTree( $tree, \%transformExTree2Compact );
}
sub CompactToExTree {
my $tree = shift;
return switchCompact( $tree, \%transformCompact2ExTree );
}
Wo% transformExTree2Compact und% transformCompact2ExTree sind Hashes mit Typ in Schlüssel und Code-ref in Wert, kann Endrekursion switchExTree oder es selfs switchCompact. Aber Dieser Ansatz ist selten wirklich brauchen, und halten müssen weniger wert College Finger weg .
Abschließend Lesbarkeit und Wartbarkeit muss vor allem in Perl und Zuordnung von @_ in einer Reihe besser ist. Wenn Sie Standardwerte wollen festlegen können Sie es tun, nur, nachdem es.
Der beste Weg, IMHO, ist eine leichte Mischung aus beiden, wie in der neuen Funktion in einem Modul:
our $Class;
sub new {
my $Class = shift;
my %opts = @_;
my $self = \%opts;
# validate %opts for correctness
...
bless $self, $Class;
}
Dann werden alle Aufruf Argumente an den Konstruktor werden als Hash übergeben, die den Code viel besser lesbar als nur eine Liste von Parametern macht.
Ich ziehe
sub factorial{
my($n) = @_;
...
}
Aus dem einfachen Grunde, dass Komodo wird dann in der Lage sein, mir zu sagen, was die Argumente sind, wenn ich gehe, es zu benutzen.