Gibt es einen Unterschied zwischen Perl-Verschiebung im Vergleich zu Zuordnung von @_ für Unterprogramm-Parameter?

StackOverflow https://stackoverflow.com/questions/415297

  •  03-07-2019
  •  | 
  •  

Frage

Damian Conways beste Praxis von nicht mehr als drei Positionsparameter für jede gegebene Unterprogramm

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.

War es hilfreich?

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.

Plus, wie

Ich vermute, dass, wenn Sie die (grob) äquivalent tun:

push @bar, shift @_ for (1 :: $big_number);

Dann Sie tun etwas falsch gemacht. Ich benutze amost immer die my ($foo, $bar) = @_; Form cos ich selbst in den Fuß geschossen habe letzteres ein paar zu viele Male mit ...

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.

scroll top