JanW2: Perl: Subroutinen aufrufen mit & oder ohne &

Hallo,

im Selfthtml-Wiki zu Perl-Subroutinen

findet sich folgender Passus

Beim Aufruf könnten Sie anstelle von Routine(); auch &Routine; oder &Routine(); notieren. Die erste Art ist jedoch am empfehlenswertesten, vor allem bei Aufrufen ohne Klammern kann es zu Fehlern kommen.

Es wird aber nicht erklärt, was für Fehler auftreten können. Bisher habe ich Subs immer mit einem & aufgerufen, da es meiner Meinung nach die Lesbarkeit des Quellcodes erhöht.

Sollte ich alle & entfernen?

Danke

  1. Sollte ich alle & entfernen?

    Nur wenn du entsprechende () ergänzt, wo sie fehlen.

    func; #ruft eine Funktion ohne Parameter auf
    &func(); #ruft eine Funktion mit Parameter auf
    func(); #ruft eine Funktion mit Parameter auf
    func; #erzeugt höchstwahrscheinlich einen Fehler, wenn func nicht als Konstante deklariert wurde.
    
    $func = sub {...} # $func ist eine Referenz auf eine anonyme Funktion
    $func->() # führt die Funktion aus.
    
    1. func; #erzeugt höchstwahrscheinlich einen Fehler, wenn func nicht als Konstante deklariert wurde.

      Das ist falsch.

  2. Lass es weg, das ist ein Stil aus längst vergangenen Zeiten und spätestens seit Majorrelease 5 überflüssig. Mit Version 5.16 krieg ich sogar eine Fehlermeldung:

    print &time();
    
    Undefined subroutine &main::time called at ...
    

    MfG

    1. Ergänzung: Das & war mal dazu gedacht, eigene Funktionen von builtin-Funktionen zu unterscheiden, z.B. so:

      printf("%s\n%s\n", time, &time);
      sub time{ "Kukuk" }
      
      Ausgabe:
      1491854024
      Kukuk
      

      Mit der Qualifizierung der Package (Namespace) wird der Code unter Anwendung des Pfeil-Operators jedoch besser lesbar:

      package Foo;
      printf("%s\n%s\n", time, Foo->time);
      sub time{ "Kukuk" }
      

      Beachte, dass mit dem Pfeiloperator eine Übergabe erfolgt, in obenstehendem Beispiel wird der Name der Package als erstes Argument übergeben. Allgemein gesagt: Methoden sind Klassenmethoden, wenn sie mit dem Namen einer Klasse (Perl-Package) aufgerufen werden. Der Konstruktor ist ein typisches Beispiel für eine Klassenmethode, er liefert eine Instanz der Klasse. Methoden die über die Instanz aufgerufen werden, sind i.d.R. Objektmethoden. MfG

  3. 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.