anonym: Perl: Subroutinen aufrufen mit & oder ohne &

Beitrag lesen

Meine Threadnachbarn haben wenig Ahnung, sie geben keine Antwort auf die Frage "was für Fehler auftreten können". Ich werde es dir zeigen. Legen wir mal die Fakten auf den Tisch:

Subroutinenaufrufe mit & umgehen Prototypes

Prototypes sind ein Merkmal aus der Frühzeit von Perl 5 und erlauben dem Programmierer, mit Referenzen durch erzwungene Typumwandlung zu tricksen, so dass es beim Aufruf so erscheint, man könne unreferenzierte Variablen angeben. Illustration:

Lass uns push nachbauen.

sub mypush {
    my (@grow_me, @more_values) = @_;
    ...
}
my @foo;
mypush @foo, qw(a s d f);

Das funktioniert nicht, weil die Argumente einer Subroutine immer eine Liste sind, und in Perl Listen ihre Werte immer falten, d.h. effektiv landet beim Destrukturieren der Parameter alles in @grow_me. Natürlich weiß jeder, dass Referenzen die Lösung zur Umgehung dieses Problems sind.

sub mypush {
    my ($grow_me_aref, @more_values) = @_;
    $grow_me_aref = [@{ $grow_me_aref }, @more_values]; # Fußnote ‡
    return scalar @{ $grow_me_aref };
}
my @foo;
mypush \\@foo, qw(a s d f);

Nun muss der Aufrufer immer daran denken, eine Referenz zu übergeben. Wenn Prototypes benutzt werden, ist das nicht mehr notwendig.

#         ↓↓↓↓↓ Prototype
sub mypush(\\@@) {
    my ($grow_me_aref, @more_values) = @_;
    ...   # der Rest wie gehabt
}
my @foo;
mypush @foo, qw(a s d f);
#      ↑ Aufruf ohne Referenz!

Von außen verhält sich der Code referenzlos, von innen mit Referenzen, Prototypes wandeln die Argumente um. Unser echtes push hat's auch so, siehe prototype.


> perl -E'say prototype "CORE::push"'

\\@@

Prototypes wurden aus Unwissenheit und wegen Cargokult auch häufig missbraucht, um in etwa nachzubilden, was Signaturen in anderen Programmiersprachen sind. Diese Praxis ist missbilligt. Man soll stattdessen Kavorka, Function::Parameters o.v.a.m. oder experimentelle Signaturen verwenden.

Fazit: Wenn eine Subroutine mit & aufgerufen wird, kommen Prototypes nicht zur Geltung. Es kann sein, dass du das möchtest, oder auch nicht. Du musst bloß über die Konsequenzen Bescheid wissen und eine bewusste Entscheidung tätigen. Besagte Fehler können auftreten, wenn du Prototypes abschaltest, sie aber vom zwingend vom ursprünglichen Programmierer der Subroutine benötigt werden.

Argumentlose Subroutinenaufrufe mit & benutzen den aktuellen Wert von @_

Gemeint ist ein Aufruf wie bspw. &foobar;. Illustration:

sub foobar {
    die "I can't deal with arguments" if @_;
    return;
}

sub main {
    my ($exit_val) = @_;
    foobar;                 # ok
    foobar();               # ok
    foobar('argument');     # dies, as expected
    &foobar();              # ok
    &foobar;                # dies unexpectedly
    return $exit_val;
}

exit(main(0));

Dies ist eine Fehlerquelle, weil der Aufruf so aussieht, als würde die Subroutine keine Argumente erhalten, aber das Gegenteil ist der Fall. Es gehen also die Intuition und die Realität auseinander.

Fazit: Verwende & für Subroutinenaufrufe nicht, es stiftet nur Verwirrung. Obwohl gut dokumentiert, wissen viele Perlprogrammierer über das Thema nicht Bescheid.

Das & kommt nicht nur bei Subroutinenaufrufen vor, wann soll man da & verwenden?

Es gibt nur noch einen Anwendungsfall, nämlich goto &NAME. Das ist ziemlich obskur und trifft man vielleicht einmal alle paar Jahre an.

Alles andere, wo man historisch & einsetzte, lässt sich besser über Coderefs oder die Universalmethode can lösen.

Fußnote ‡

Diese Zeile Code funktioniert nicht wirklich aus mangelndem Aliasing. Ich hab's weggelassen, es würde nur von der Erklärung ablenken.