Kapitel 4. Unterroutinen

Diese Arbeit wurde mithilfe von KI übersetzt. Wir freuen uns über dein Feedback und deine Kommentare: translation-feedback@oreilly.com

Du hast bereits einige der eingebauten Systemfunktionen wie chomp, reverse, print und so weiter kennengelernt und benutzt. Aber wie andere Sprachen auch, kann Perl Unterprogramme erstellen, die benutzerdefinierte Funktionen sind. Damit kannst du ein Stück Code mehrmals in einem Programm wiederverwenden. Der Name einer Subroutine ist ein weiterer Perl-Bezeichner (Buchstaben, Ziffern und Unterstriche, die aber nicht mit einer Ziffer beginnen dürfen) mit einem vorangestellten, manchmal optionalen kaufmännischen Und-Zeichen (&). Es gibt eine Regel, die besagt, wann du das kaufmännische Und-Zeichen weglassen kannst und wann nicht; du wirst sie am Ende des Kapitels kennenlernen. Fürs Erste solltest du es immer dann verwenden, wenn es nicht verboten ist, was immer eine sichere Regel ist. Wir werden dir natürlich sagen, wo es verboten ist.

Der Name des Unterprogramms kommt aus einem separaten Namensraum, damit Perl nicht verwirrt wird, wenn du ein Unterprogramm namens &fred und einen Skalar namens $fred im selben Programm hast - obwohl es unter normalen Umständen keinen Grund gibt, das zu tun.

Definieren einer Unterroutine

Um dein eigenes Unterprogramm zu definieren, verwendest du das Schlüsselwort sub, den Namen des Unterprogramms (ohne das kaufmännische Und) und dann den Codeblock in geschweiften Klammern, der den Hauptteil des Unterprogramms bildet. Zum Beispiel so:

sub marine {
  $n += 1;  # Global variable $n
  print "Hello, sailor number $n!\n";
}

Du kannst deine Unterprogrammdefinitionen überall in deinem Programmtext unterbringen, aber Programmierer, die mit Sprachen wie C oder Pascal vertraut sind, setzen sie gerne an den Anfang der Datei. Andere bevorzugen es, sie an das Ende der Datei zu setzen, damit der Hauptteil des Programms am Anfang erscheint. Das bleibt dir überlassen. In jedem Fall brauchst du normalerweise keine Vorwärtsdeklaration. Wenn du zwei Unterprogrammdefinitionen mit demselben Namen hast, überschreibt die spätere die frühere. Wenn du allerdings Warnungen aktiviert hast, wird Perl dich darauf hinweisen, wenn du das tust. Im Allgemeinen gilt das als schlechte Form oder als Zeichen eines verwirrten Programmierers.

Hinweis

Über gleichnamige Unterprogramme in verschiedenen Paketen sprechen wir erst in Intermediate Perl.

Wie du im vorherigen Beispiel vielleicht bemerkt hast, kannst du alle globalen Variablen innerhalb des Unterprogrammkörpers verwenden. Tatsächlich sind alle Variablen, die du bisher gesehen hast, global, d.h. sie sind von jedem Teil deines Programms aus zugänglich. Linguistische Puristen sind darüber entsetzt, aber das Perl-Entwicklungsteam hat schon vor Jahren einen wütenden Mob mit Fackeln gebildet und sie aus der Stadt gejagt. Wie du private Variablen erstellen kannst, erfährst du im Abschnitt "Private Variablen in Unterprogrammen".

Aufrufen einer Unterroutine

Du rufst ein Unterprogramm aus einem Ausdruck heraus auf, indem du den Namen des Unterprogramms (mit dem kaufmännischen Und) verwendest:

&marine;  # says Hello, sailor number 1!
&marine;  # says Hello, sailor number 2!
&marine;  # says Hello, sailor number 3!
&marine;  # says Hello, sailor number 4!

Meistens bezeichnest du den Aufruf einfach als Aufruf der Unterroutine. Du wirst im Laufe dieses Kapitels auch andere Möglichkeiten kennenlernen, wie du das Unterprogramm aufrufen kannst.

Rückgabewerte

Du rufst ein Unterprogramm immer als Teil eines Ausdrucks auf, auch wenn du das Ergebnis des Ausdrucks nicht verwendest. Als du vorhin &marine aufgerufen hast, hast du den Wert des Ausdrucks, der den Aufruf enthält, berechnet und dann das Ergebnis weggeworfen.

Oftmals rufst du ein Unterprogramm auf und machst etwas mit dem Ergebnis. Das bedeutet, dass du etwas mit dem Rückgabewert der Subroutine machst. Alle Perl-Subroutinen haben einen Rückgabewert - es gibt keinen Unterschied zwischen denen, die Werte zurückgeben, und denen, die das nicht tun. Allerdings haben nicht alle Perl-Unterprogramme einen sinnvollenRückgabewert.

Da du Perl-Unterprogramme so aufrufen kannst, dass sie einen Rückgabewert benötigen, wäre es etwas verschwenderisch, eine spezielle Syntax zu deklarieren, um in den meisten Fällen einen bestimmten Wert "zurückzugeben". Also hat Larry es einfach gemacht. Während Perl in einer Subroutine arbeitet, berechnet es im Rahmen seiner Aktionen Werte. Die letzte Berechnung, die in einem Unterprogramm durchgeführt wird, ist automatisch auch der Rückgabewert.

Diese Unterroutine hat zum Beispiel eine Addition als letzten Ausdruck:

sub sum_of_fred_and_barney {
  print "Hey, you called the sum_of_fred_and_barney subroutine!\n";
  $fred + $barney;  # That's the return value
}

Der letzte ausgewertete Ausdruck im Körper dieser Unterroutine ist die Summe von $fred und $barney, also ist die Summe von $fred und $barney der Rückgabewert. Hier siehst du das in Aktion:

$fred   = 3;
$barney = 4;
$wilma  = &sum_of_fred_and_barney;      # $wilma gets 7
print "\$wilma is $wilma.\n";

$betty  = 3 * &sum_of_fred_and_barney;  # $betty gets 21
print "\$betty is $betty.\n";

Dieser Code erzeugt diese Ausgabe:

Hey, you called the sum_of_fred_and_barney subroutine!
$wilma is 7.
Hey, you called the sum_of_fred_and_barney subroutine!
$betty is 21.

Die Anweisung print ist nur ein Hilfsmittel zur Fehlersuche, damit du sehen kannst, dass du das Unterprogramm aufgerufen hast. Normalerweise nimmst du diese Art von Anweisungen heraus, wenn du dein Programm einsetzen willst. Aber nehmen wir an, du hast eine weitere print am Ende des Unterprogramms hinzugefügt, etwa so:

sub sum_of_fred_and_barney {
  print "Hey, you called the sum_of_fred_and_barney subroutine!\n";
  $fred + $barney;  # That's not really the return value!
  print "Hey, I'm returning a value now!\n";      # Oops!
}

Der letzte ausgewertete Ausdruck ist nicht mehr die Addition, sondern die Anweisung print, deren Rückgabewert normalerweise 1 lautet, was "Drucken war erfolgreich" bedeutet, aber das ist nicht der Rückgabewert, den du eigentlich wolltest. Sei also vorsichtig, wenn du zusätzlichen Code zu einem Unterprogramm hinzufügst, denn der letzte ausgewertete Ausdruck wird der Rückgabewert sein.

Hinweis

Der Rückgabewert von print ist true bei einer erfolgreichen Operation und false bei einem Fehler. Wie du die Art des Fehlers bestimmst, erfährst du in Kapitel 5.

Was ist mit der Summe aus $fred und $barney in der zweiten (fehlerhaften) Unterroutine passiert? Du hast sie nirgendwo eingefügt, also hat Perl sie verworfen. Wenn du eine Warnung angefordert hättest, hätte Perl (da es bemerkt hat, dass es nichts Sinnvolles ist, zwei Variablen zu addieren und das Ergebnis zu verwerfen) dich wahrscheinlich gewarnt, z. B. vor einer "nutzlosen Verwendung der Addition in einem ungültigen Kontext". Der Begriff " ungültiger Kontext" ist nur eine schicke Umschreibung dafür, dass du die Antwort nicht verwendest, sei es, dass du sie in einer Variablen speicherst oder sie auf andere Weise nutzt.

Mit dem "zuletzt ausgewerteten Ausdruck" ist eigentlich der letzte Ausdruck gemeint, den Perl auswertet, und nicht die letzte Anweisung im Unterprogramm. Zum Beispiel gibt dieses Unterprogramm den größeren Wert von $fred oder $barney zurück:

sub larger_of_fred_or_barney {
  if ($fred > $barney) {
    $fred;
  } else {
    $barney;
  }
}

Der letzte ausgewertete Ausdruck ist entweder $fred oder $barney, also wird der Wert einer dieser Variablen zum Rückgabewert. Du weißt nicht, ob der Rückgabewert $fred oder $barney sein wird, bis du siehst, was diese Variablen zur Laufzeit enthalten.

Das sind alles eher triviale Beispiele. Besser wird es, wenn du einem Unterprogramm Werte übergeben kannst, die sich bei jedem Aufruf unterscheiden, anstatt sich auf globale Variablen zu verlassen. Dazu kommen wir gleich.

Argumente

Das Unterprogramm larger_of_fred_or_barney wäre viel nützlicher, wenn es dich nicht zwingen würde, die globalen Variablen $fred und $barney zu verwenden. Wenn du den größeren Wert von $wilma und $betty abrufen möchtest, musst du diese Variablen erst in $fred und $barney kopieren, bevor du larger_of_fred_or_barney verwenden kannst. Und wenn du etwas Nützliches in diesen Variablen hättest, müsstest du sie zuerst in andere Variablen kopieren - zum Beispiel in $save_fred und $save_barney. Und wenn du mit dem Unterprogramm fertig bist, musst du sie wieder zurück nach $fred und $barney kopieren.

Zum Glück gibt es in Perl Unterprogrammargumente. Um eine Argumentliste an das Unterprogramm zu übergeben, setzt du den Listenausdruck einfach in Klammern hinter den Unterprogrammaufruf, etwa so:

$n = &max(10, 15);  # This sub call has two parameters

Perl übergibt die Liste an das Unterprogramm, d.h. Perl stellt die Liste dem Unterprogramm zur Verfügung, damit es sie verwenden kann, wie es will. Natürlich musst du diese Liste irgendwo speichern. Deshalb speichert Perl die Parameterliste (ein anderer Name für die Argumentenliste) automatisch in der speziellen Array-Variablen namens @_ für die Dauer des Unterprogramms. Du kannst auf dieses Array zugreifen, um sowohl die Anzahl der Argumente als auch den Wert dieser Argumente zu ermitteln.

Das bedeutet, dass der erste Parameter des Unterprogramms in $_[0] gespeichert ist, der zweite in $_[1] und so weiter. Aber - und das ist ein wichtiger Hinweis - diese Variablen haben nichts mit der Variablen $_ zu tun, genauso wenig wie $dino[3] (ein Element des Arrays @dino ) mit $dino (einer völlig anderen Skalarvariablen). Die Parameterliste muss sich in einer Array-Variablen befinden, damit dein Unterprogramm sie verwenden kann, und Perl verwendet zu diesem Zweck das Array @_.

Nun könntest du das Unterprogramm &max so schreiben, dass es ein bisschen wie das Unterprogramm &larger_of_fred_or_barney aussieht, aber statt $fredkönntest du den ersten Unterprogrammparameter ($_[0]) verwenden und statt $barneykönntest du den zweiten Unterprogrammparameter ($_[1]) verwenden. Und so könntest du am Ende etwas wie das hier bekommen:

sub max {
  # Compare this to &larger_of_fred_or_barney
  if ($_[0] > $_[1]) {
    $_[0];
  } else {
    $_[1];
  }
}

Nun, wie wir schon sagten, kannst du das tun. Aber es ist ziemlich hässlich mit all den Teilstrichen und außerdem schwer zu lesen, zu schreiben, zu prüfen und zu debuggen. Du wirst gleich einen besseren Weg sehen.

Es gibt noch ein weiteres Problem mit dieser Subroutine. Der Name &max ist nett und kurz, aber er erinnert uns nicht daran, dass dieses Unterprogramm nur richtig funktioniert, wenn es mit genau zwei Parametern aufgerufen wird:

$n = &max(10, 15, 27);  # Oops!

max ignoriert die zusätzlichen Parameter, da es nie auf $_[2] schaut. Perl ist es egal, ob dort etwas drin steht oder nicht. Perl kümmert sich auch nicht um unzureichende Parameter - du bekommst einfach undef, wenn du über das Ende des @_ Arrays hinausschaust, wie bei jedem anderen Array auch. Wie du eine bessere &max erstellst, die mit einer beliebigen Anzahl von Parametern funktioniert, erfährst du später in diesem Kapitel.

Die Variable @_ ist für das Unterprogramm privat; wenn es einen globalen Wert in @_ gibt, speichert Perl ihn, bevor es das nächste Unterprogramm aufruft, und stellt den vorherigen Wert bei der Rückkehr aus diesem Unterprogramm wieder her. Das bedeutet auch, dass eine Subroutine Argumente an eine andere Subroutine weitergeben kann, ohne Angst zu haben, dass ihre eigene @_ Variable verloren geht - der verschachtelte Subroutinenaufruf erhält seine eigene @_ auf die gleiche Weise. Auch wenn das Unterprogramm sich selbst rekursiv aufruft, erhält jeder Aufruf eine neue @_, sodass @_ immer die Parameterliste für den aktuellen Unterprogrammaufruf ist.

Hinweis

Du erkennst vielleicht, dass dies derselbe Mechanismus ist, der auch für die Kontrollvariable der foreach-Schleife verwendet wird, wie in Kapitel 3 beschrieben. In beiden Fällen wird der Wert der Variablen gespeichert und von Perl automatisch wiederhergestellt.

Private Variablen in Unterroutinen

Aber wenn Perl dir bei jedem Aufruf eine neue @_ geben kann, kann es dir dann nicht auch Variablen für deine eigene Verwendung geben? Natürlich kann es das.

Standardmäßig sind alle Variablen in Perl globale Variablen, das heißt, sie sind von jedem Teil des Programms aus zugänglich. Du kannst aber jederzeit mit dem Operator my private Variablen, sogenannte lexikalische Variablen, erstellen:

sub max {
  my($m, $n);       # new, private variables for this block
  ($m, $n) = @_;    # give names to the parameters
  if ($m > $n) { $m } else { $n }
}

Diese Variablen sind privat (oder "scoped") für den umschließenden Block; jeder andere $m oder $n ist von diesen beiden Variablen völlig unbeeinflusst. Und das gilt auch umgekehrt - kein anderer Code kann auf diese privaten Variablen zugreifen oder sie verändern, sei es aus Versehen oder absichtlich. Du kannst dieses Unterprogramm also in jedes beliebige Perl-Programm auf der Welt einfügen und sicher sein, dass du die $m und $n (falls vorhanden) des Programms nicht durcheinander bringst. Wenn das Programm bereits ein Unterprogramm namens &max hat, würdest du es natürlich durcheinander bringen.

Es ist auch erwähnenswert, dass du in diesen if Blöcken kein Semikolon nach dem Rückgabewertausdruck brauchst. Das Semikolon ist eigentlich ein Anweisungstrenner und kein Anweisungsende. Perl erlaubt es dir zwar, das letzte Semikolon in einem Block wegzulassen, aber in der Praxis lässt du es nur weg, wenn der Code so einfach ist, dass du den Block in einer einzigen Zeile schreiben kannst.

Du kannst das Unterprogramm aus dem vorherigen Beispiel noch einfacher gestalten. Ist dir aufgefallen, dass die Liste ($m, $n)zweimal auftaucht? Du kannst den Operator myauf eine Liste von Variablen anwenden, die in Klammern eingeschlossen sind und die du in einer Listenzuweisung verwendest, daher ist es üblich, diese ersten beiden Anweisungen im Unterprogramm zu kombinieren:

my($m, $n) = @_;  # Name the subroutine parameters

Mit dieser einen Anweisung werden die privaten Variablen erstellt und ihre Werte gesetzt, so dass der erste Parameter jetzt den einfacher zu verwendenden Namen $m und der zweite den $n hat. Fast jedes Unterprogramm beginnt mit einer Zeile wie dieser, in der die Parameter genannt werden. Wenn du diese Zeile siehst, weißt du, dass die Subroutine zwei skalare Parameter erwartet, die du innerhalb der Subroutine $m und $n nennen wirst.

Parameterlisten mit variabler Länge

Im realen Perl-Code haben Unterprogramme oft Parameterlisten von beliebiger Länge. Das liegt an der Perl-Philosophie "keine unnötigen Grenzen", die du bereits kennengelernt hast. Das ist natürlich anders als bei vielen traditionellen Programmiersprachen, bei denen jedes Unterprogramm streng typisiert sein muss, d.h. nur eine bestimmte Anzahl von Parametern mit vordefinierten Typen zulassen darf. Es ist schön, dass Perl so flexibel ist, aber (wie du schon bei der &max Routine gesehen hast) kann das zu Problemen führen, wenn du ein Unterprogramm mit einer anderen Anzahl von Argumenten aufrufst, als es erwartet.

Natürlich kannst du leicht überprüfen, ob das Unterprogramm die richtige Anzahl von Argumenten hat, indem du das Array @_untersuchst. Du hättest &max zum Beispiel so schreiben können, dass es seine Argumentliste überprüft:

sub max {
  if (@_ != 2) {
    print "WARNING! &max should get exactly two arguments!\n";
  }
  # continue as before...
}

Der if Test verwendet den "Namen" des Arrays in einem skalaren Kontext, um die Anzahl der Arrayelemente zu ermitteln, wie du in Kapitel 3 gesehen hast.

Aber in der realen Welt der Perl-Programmierung benutzt kaum jemand diese Art der Überprüfung; es ist besser, wenn sich deine Unterprogramme an die Parameter anpassen.

Eine bessere &max Routine

Schreibe &max so um, dass es eine beliebige Anzahl von Argumenten zulässt, sodass du es wie folgt aufrufen kannst:

$maximum = &max(3, 5, 10, 4, 6);

sub max {
  my($max_so_far) = shift @_;  # the first one is the largest yet seen
  foreach (@_) {               # look at the remaining arguments
    if ($_ > $max_so_far) {  # could this one be bigger yet?
      $max_so_far = $_;
    }
  }
  $max_so_far;
}

Dieser Code verwendet den sogenannten Hochwassermarken-Algorithmus: Nach einer Überschwemmung, wenn das Wasser zum letzten Mal gestiegen und zurückgegangen ist, zeigt die Hochwassermarke an, wo der höchste Wasserstand erreicht wurde. In dieser Routine speichert $max_so_fardie Hochwassermarke, also den höchsten Wert, in der Variablen $max_so_far.

Die erste Zeile setzt $max_so_farauf 3 (den ersten Parameter im Beispielcode), indem sie diesen Parameter aus dem Parameter-Array, @_, verschiebt. @_ enthält jetzt also (5, 10, 4, 6), da du 3 entfernt hast. Und die größte Zahl, die du bisher gesehen hast, ist die einzige, die du bisher gesehen hast: 3, der erste Parameter.

Als Nächstes durchläuft die Schleife foreach die verbleibenden Werte in der Parameterliste, beginnend mit @_. Die Kontrollvariable der Schleife ist standardmäßig $_. (Aber erinnere dich, es gibt keine automatische Verbindung zwischen @_und $_; es ist nur ein Zufall, dass sie so ähnliche Namen haben.) Beim ersten Durchlauf der Schleife ist $_ gleich 5. Der if Test stellt fest, dass er größer ist als $max_so_far und setzt $max_so_far auf 5- die neue Höchstmarke.

Beim nächsten Durchlauf der Schleife ist $_ 10 . Das ist ein neuer Höchststand, also speicherst du ihn auch in $max_so_far.

Beim nächsten Mal ist $_ gleich 4. Der ifTest schlägt fehl, da er nicht größer ist als $max_so_far, was 10 ist, also überspringst du den Körper von if.

Zum Schluss wird $_ zu 6, und du überspringst wieder den Körper von if. Das war das letzte Mal, dass du die Schleife durchlaufen hast, also ist die Schleife fertig.

Jetzt wird $max_so_far der Rückgabewert. Es ist die größte Zahl, die du gesehen hast, und du hast sie alle gesehen, also muss es die größte aus der Liste sein: 10.

Leere Parameterlisten

Der verbesserte &maxAlgorithmus funktioniert jetzt auch, wenn es mehr als zwei Parameter gibt. Aber was passiert, wenn es keine gibt?

Auf den ersten Blick mag es zu esoterisch erscheinen, um sich darüber Gedanken zu machen. Denn warum sollte jemand &max aufrufen, ohne ihm Parameter zu geben? Aber vielleicht hat jemand eine Zeile wie diese geschrieben:

$maximum = &max(@numbers);

Und das Array @numbers könnte manchmal eine leere Liste sein; vielleicht wurde es aus einer Datei eingelesen, die sich als leer herausstellte. Du musst also wissen: Was macht &max in diesem Fall?

Die erste Zeile der Subroutine setzt $max_so_far, indem sie shift auf @_, das (jetzt leere) Parameter-Array, anwendet. Das ist harmlos; das Array bleibt leer, und shift gibt undef an $max_so_far zurück.

Die Schleife foreach will nun über @_ iterieren, aber da diese leer ist, führst du den Schleifenkörper null Mal aus.

Also gibt Perl den Wert von $max_so_far-undef- als Rückgabewert der Subroutine zurück. In gewisser Weise ist das die richtige Antwort, denn es gibt keinen größten (Nicht-)Wert in einer leeren Liste.

Natürlich sollte sich derjenige, der dieses Unterprogramm aufgerufen hat, darüber im Klaren sein, dass der Rückgabewert undefsein kann - oder er könnte einfach sicherstellen, dass die Parameterliste nie leer ist.

Anmerkungen zu lexikalischen (meinen) Variablen

Diese lexikalischen Variablen können eigentlich in jedem Block verwendet werden, nicht nur im Block eines Unterprogramms. Sie können zum Beispiel im Block eines if, while oder foreach verwendet werden:

foreach (1..10) {
  my($square) = $_ * $_;  # private variable in this loop
  print "$_ squared is $square.\n";
}

Die Variable $square ist privat für den umschließenden Block; in diesem Fall ist der Block der foreach Schleife. Wenn es keinen umschließenden Block gibt, ist die Variable für die gesamte Quelldatei privat.

Im Moment werden deine Programme nicht mehr als eine Quelldatei verwenden, also ist das kein Problem. Wichtig ist jedoch, dass die scope einer lexikalischen Variablen auf den kleinsten umschließenden Block oder die kleinste Datei beschränkt ist. Der einzige Code, der $square sagen und diese Variable meinen kann, ist der Code innerhalb dieses textuellen Bereichs.

Das ist ein großer Gewinn für die Wartungsfreundlichkeit - wenn du einen falschen Wert in $square findest, solltest du den Übeltäter auch in einer begrenzten Menge an Quellcode finden. Wie erfahrene Programmierer/innen (oft auf die harte Tour) gelernt haben, beschleunigt die Beschränkung des Geltungsbereichs einer Variablen auf eine Seite Code oder sogar auf ein paar Codezeilen den Entwicklungs- und Testzyklus erheblich.

Eine Datei ist auch ein Bereich, also ist eine lexikalische Variable in einer Datei in einer anderen nicht sichtbar. Die Behandlung von wiederverwendbaren Bibliotheken und Modulen verschieben wir jedoch auf Intermediate Perl.

Beachte auch, dass der my Operator den Kontext einer Zuweisung nicht verändert:

my($num) = @_;  # list context, same as ($num) = @_;
my $num  = @_;  # scalar context, same as $num = @_;

In der ersten Zeile erhält $num den ersten Parameter als Listenkontext-Zuweisung, in der zweiten Zeile die Anzahl der Parameter in einem skalaren Kontext. Jede der beiden Codezeilen könnte das sein, was der Programmierer wollte; du kannst das aber nicht anhand dieser einen Zeile erkennen und Perl kann dich daher nicht warnen, wenn du die falsche Zeile verwendest. (Natürlich würden diese beiden Zeilen nicht im selben Unterprogramm stehen, da man nicht zwei lexikalische Variablen mit demselben Namen im selben Bereich deklarieren kann; dies ist nur ein Beispiel.) Wenn du also Code wie diesen liest, kannst du immer den Kontext der Zuweisung erkennen, indem du siehst, wie der Kontext ohne das Wort my aussehen würde.

Erinnere dich daran, dass my ohne die Klammern nur eine einzigelexikalische Variable deklariert:

my $fred, $barney;      # WRONG! Fails to declare $barney
my($fred, $barney);     # declares both

Natürlich kannst du mit my auch neue, private Arrays erstellen:

my @phone_number;

Jede neue Variable ist zunächst leer -undef für Skalare oder die leere Liste für Arrays.

In der normalen Perl-Programmierung wirst du wahrscheinlich my verwenden, um eine neue Variable in einem Bereich einzuführen. In Kapitel 3 hast du gesehen, dass du deine eigene Kontrollvariable mit der foreachStruktur definieren kannst. Du kannst sie auch zu einer lexikalischen Variable machen:

foreach my $rock (qw/ bedrock slate lava /) {
  print "One rock is $rock.\n";  # Prints names of three rocks
}

Das ist wichtig für den nächsten Abschnitt, in dem du eine Funktion verwendest, die dich dazu bringt, alle deine Variablen zu deklarieren.

Die Verwendung strenger Pragma

Perl ist in der Regel eine ziemlich freizügige Sprache. Aber vielleicht möchtest du, dass Perl dir ein wenig Disziplin auferlegt; das kannst du mit dem use strict pragma erreichen.

Ein Pragma ist ein Hinweis für einen Compiler, der ihm etwas über den Code mitteilt. In diesem Fall sagt das use strict pragma dem internen Compiler von Perl, dass er für den Rest dieses Blocks oder der Quelldatei einige gute Programmierregeln durchsetzen soll.

Warum sollte das wichtig sein? Nun, stell dir vor, du schreibst dein Programm und tippst eine Zeile wie diese:

$bamm_bamm = 3;  # Perl creates that variable automatically

Jetzt tippst du eine Weile weiter. Nachdem die Zeile am oberen Rand des Bildschirms verschwunden ist, tippst du diese Zeile, um die Variable zu erhöhen:

$bammbamm += 1;  # Oops!

Da Perl einen neuen Variablennamen erkennt (der Unterstrich ist wichtig in einem Variablennamen), erstellt es eine neue Variable und erhöht diese. Wenn du Glück hast und schlau bist, hast du die Warnungen aktiviert und Perl kann dir sagen, dass du einen oder beide dieser globalen Variablennamen nur ein einziges Mal in deinem Programm verwendet hast. Wenn du aber nur schlau bist, hast du jeden Namen mehr als einmal verwendet, und Perl kann dich nicht warnen.

Um Perl mitzuteilen, dass du bereit bist, restriktiver zu sein, fügst du das Pragma use strict am Anfang deines Programms ein (oder in jedem Block oder jeder Datei, in der du diese Regeln durchsetzen willst):

use strict;  # Enforce some good programming rules

Ab Perl 5.12 verwendest du dieses Pragma implizit, wenn du eine Mindestversion von Perl angibst:

use v5.12; # loads strict for you

Neben anderen Einschränkungen besteht Perl nun darauf, dass du jede neue Variable deklarierst, was normalerweise mit my geschieht:

my $bamm_bamm = 3;  # New lexical variable

Wenn du nun versuchst, es anders zu schreiben, erkennt Perl die Probleme und beschwert sich, dass du keine Variable mit dem Namen $bammbamm deklariert hast, sodass dein Fehler bei der Kompilierung automatisch erkannt wird:

$bammbamm += 1;  # No such variable: Compile time fatal error

Das gilt natürlich nur für neue Variablen; die in Perl eingebauten Variablen wie $_ und @_ musst du nicht deklarieren. Wenn du use strict zu einem bereits geschriebenen Programm hinzufügst, bekommst du in der Regel eine Flut von Warnmeldungen, deshalb ist es besser, sie von Anfang an zu verwenden, wenn sie gebraucht wird.

Hinweis

use strict prüft die Variablen mit den Namen $a und $b nicht, weil sort diese globalen Variablen verwendet. Das sind sowieso keine besonders guten Variablennamen.

Die meisten Leute empfehlen, dass Programme, die länger als ein Bildschirm voll Text sind, generell use strict benötigen. Und wir stimmen zu.

Von nun an werden wir die meisten (aber nicht alle) Beispiele so schreiben, als ob use strict in Kraft ist, auch wenn wir es nicht zeigen. Das heißt, wir werden Variablen in der Regel mit my deklarieren, wenn es angebracht ist. Auch wenn wir das hier nicht immer tun, möchten wir dich ermutigen , use strict so oft wie möglich in deinen Programmen zu verwenden. Du wirst es uns auf lange Sicht danken.

Der Rückgabe-Operator

Was ist, wenn du dein Unterprogramm sofort beenden willst? Der return Operator gibt sofort einen Wert aus einem Unterprogramm zurück:

my @names = qw/ fred barney betty dino wilma pebbles bamm-bamm /;
my $result = &which_element_is("dino", @names);

sub which_element_is {
  my($what, @array) = @_;
  foreach (0..$#array) {  # indices of @array's elements
    if ($what eq $array[$_]) {
      return $_;         # return early once found
    }
  }
  -1;                    # element not found (return is optional here)
}

Du bittest diese Unterroutine, den Index von dino im Array @names zu finden. Zunächst werden in der Deklaration my die Parameter genannt: $what ist das, wonach du suchst, und @array ist ein Array mit Werten, in dem du suchen kannst. Das ist in diesem Fall eine Kopie des Arrays @names. Die Schleife foreach durchläuft die Indizes von @array (der erste Index ist 0, der letzte ist $#array, wie du in Kapitel 3 gesehen hast).

Jedes Mal, wenn du die Schleife foreachdurchläufst, überprüfst du, ob die Zeichenkette in $what gleich dem Element aus @array am aktuellen Index ist. Wenn sie gleich ist, gibst du diesen Index sofort zurück. Dies ist die häufigste Verwendung des Schlüsselworts return in Perl - um einen Wert sofort zurückzugeben, ohne den Rest des Unterprogramms auszuführen.

Was aber, wenn du das Element nie gefunden hast? In diesem Fall hat sich der Autor dieser Unterroutine dafür entschieden, -1 als "Wert nicht gefunden" zurückzugeben. Perliger wäre es vielleicht, in diesem Fall undef zurückzugeben, aber dieser Programmierer hat -1 verwendet. return -1 in der letzten Zeile zu sagen, wäre korrekt, aber das Wort returnwird nicht wirklich gebraucht.

Manche Programmierer verwenden return jedes Mal, wenn es einen Rückgabewert gibt, um zu dokumentieren, dass es sich um einen Rückgabewert handelt. Du könntest zum Beispiel return verwenden, wenn der Rückgabewert nicht die letzte Zeile des Unterprogramms ist, wie in dem Unterprogramm &larger_of_fred_or_barney, weiter oben in diesem Kapitel. Du brauchst es nicht wirklich, aber es schadet auch nicht. Viele Perl-Programmierer glauben jedoch, dass es sich nur um sieben zusätzliche Zeichen beim Tippen handelt.

Das Ampersand weglassen

Wie versprochen, verraten wir dir jetzt die Regel, wann du das kaufmännische Und bei einem Unterprogrammaufruf weglassen kannst. Wenn der Compiler die Definition des Unterprogramms vor dem Aufruf sieht oder wenn Perl anhand der Syntax erkennen kann, dass es sich um einen Unterprogrammaufruf handelt, kann das Unterprogramm wie eine eingebaute Funktion ohne ein kaufmännisches Und aufgerufen werden. (Aber diese Regel hat einen Haken, wie du gleich sehen wirst).

Das heißt, wenn Perl allein anhand der Syntax erkennen kann, dass es sich um einen Unterprogrammaufruf ohne das kaufmännische Und handelt, ist das in der Regel in Ordnung. Das heißt, wenn du die Parameterliste in Klammern hast, muss es sich um einen Funktionsaufruf handeln:

my @cards = shuffle(@deck_of_cards);  # No & necessary on &shuffle

In diesem Fall ist die Funktion die Unterroutine &shuffle. Es kann sich aber auch um eine eingebaute Funktion handeln, wie du gleich sehen wirst.

Wenn der interne Compiler von Perl die Definition des Unterprogramms bereits gesehen hat, ist das normalerweise auch in Ordnung. In diesem Fall kannst du sogar die Klammern um die Argumentliste weglassen:

sub division {
  $_[0] / $_[1];                   # Divide first param by second
}

my $quotient = division 355, 113;  # Uses &division

Das funktioniert aufgrund der Regel, dass du Klammern immer weglassen kannst, wenn sie die Bedeutung des Codes nicht verändern. Du kannst die Klammern aber nicht weglassen, wenn du die & verwendest.

Füge die Deklaration des Unterprogramms jedoch nicht nach dem Aufruf ein; wenn du das tust, weiß der Compiler nicht, worum es bei dem versuchten Aufruf von division geht. Der Compiler muss die Definition vor dem Aufruf sehen, damit er den Unterprogrammaufruf so verwenden kann, als wäre er ein Built-in. Andernfalls weiß der Compiler nicht, was er mit diesem Ausdruck machen soll, weil er noch nicht weiß, was division ist.

Hinweis

Wie bei vielen Dingen in diesem Buch ist die Verwendung von & sinnvoll, um dir die Sprache Perl beizubringen, und nicht, um dich zu einem erfahrenen Perl-Programmierer zu machen. Manche Leute sind da anderer Meinung. Mehr dazu erfährst du im Blogbeitrag "Warum wir das Subroutinen-Ampersand lehren".

Das ist aber nicht der Haken. Der Haken an der Sache ist folgender: Wenn das Unterprogramm den gleichen Namen wie ein Perl-Integral hat, musst du das kaufmännische Und verwenden, um deine Version aufzurufen. Mit einem Hochkomma kannst du sicher sein, dass du das Unterprogramm aufrufen kannst; ohne Hochkomma kannst du das Unterprogramm nur aufrufen, wenn es kein Built-in mit demselben Namen gibt:

sub chomp {
  print "Munch, munch!\n";
}

&chomp;  # That ampersand is not optional!

Ohne das kaufmännische Und-Zeichen würdest du die integrierte Funktion chomp aufrufen, obwohl du das Unterprogramm &chomp definiert hast. Die eigentliche Regel lautet also: Bis du die Namen aller eingebauten Perl-Funktionen kennst, solltest du bei Funktionsaufrufen das kaufmännische Und verwenden. Das bedeutet, dass du es für deine ersten hundert oder so Programme verwenden wirst. Aber wenn du siehst, dass jemand anderes das kaufmännische Und in seinem eigenen Code weggelassen hat, ist das kein Fehler; vielleicht weiß er einfach, dass Perl keine eingebaute Funktion mit diesem Namen hat .

Nicht-skalare Rückgabewerte

Ein Skalar ist nicht die einzige Art von Rückgabewert, die eine Subroutine haben kann. Wenn du deine Unterroutine in einem Listenkontext aufrufst, kann sie eine Liste von Werten zurückgeben.

Hinweis

Mit der Funktion wantarraykannst du feststellen, ob ein Unterprogramm in einem skalaren oder einem Listenkontext ausgewertet wird. So kannst du ganz einfach Unterprogramme mit bestimmten Listen- oder Skalarkontextwerten schreiben.

Angenommen, du möchtest einen Zahlenbereich erhalten (wie mit dem Bereichsoperator ..), nur dass du nicht nur aufwärts, sondern auch abwärts zählen können möchtest. Der Bereichsoperator zählt nur aufwärts, aber das lässt sich leicht ändern:

sub list_from_fred_to_barney {
  if ($fred < $barney) {
    # Count upward from $fred to $barney
    $fred..$barney;
  } else {
    # Count downward from $fred to $barney
    reverse $barney..$fred;
  }
}

$fred = 11;
$barney = 6;
@c = &list_from_fred_to_barney; # @c gets (11, 10, 9, 8, 7, 6)

In diesem Fall gibt dir der Bereichsoperator die Liste von 6 bis 11, dann kehrt reverse die Liste um, so dass sie von $fred (11) bis $barney (6) geht, genau wie wir es wollten.

Das Mindeste, was du zurückgeben kannst, ist gar nichts. Ein return ohne Argumente gibt undef in einem skalaren Kontext oder eine leere Liste in einem Listenkontext zurück. Das kann nützlich sein, wenn ein Unterprogramm einen Fehler zurückgibt, um dem Aufrufer zu signalisieren, dass ein sinnvoller Rückgabewert nicht verfügbar ist.

Persistente, private Variablen

Mit my konntest du Variablen für ein Unterprogramm privat machen, obwohl du sie jedes Mal, wenn du das Unterprogramm aufriefst, neu definieren musstest. Mit state kannst du private Variablen immer noch auf das Unterprogramm beschränken, aber Perl behält ihre Werte zwischen den Aufrufen.

Zurück zum ersten Beispiel in diesem Kapitel: Du hattest ein Unterprogramm namens marine, das eine Variable inkrementiert:

sub marine {
  $n += 1;  # Global variable $n
  print "Hello, sailor number $n!\n";
}

Jetzt, da du strict kennst, fügst du sie zu deinem Programm hinzu und stellst fest, dass deine Verwendung der globalen Variable $n nun ein Kompilierungsfehler ist. Du kannst $n nicht zu einer lexikalischen Variable mit my machen, weil sie ihren Wert zwischen den Aufrufen nicht beibehalten würde.

Indem wir unsere Variable mit statedeklarieren, weisen wir Perl an, den Wert der Variable zwischen den Aufrufen des Unterprogramms beizubehalten und die Variable für das Unterprogramm privat zu machen. Diese Funktion wurde in Perl 5.10 eingeführt:

use v5.10;

sub marine {
  state $n = 0;  # private, persistent variable $n
  $n += 1;
  print "Hello, sailor number $n!\n";
}

Jetzt kannst du die gleiche Ausgabe erhalten, obwohl du strict-clean bist und keine globale Variable verwendest. Beim ersten Aufruf der Subroutine deklariert und initialisiert Perl $n. Bei allen folgenden Aufrufen ignoriert Perl diese Anweisung. Zwischen den Aufrufen behält Perl den Wert von $n für den nächsten Aufruf der Unterroutine bei.

Du kannst jeden Variablentyp zu einer state Variable machen; das gilt nicht nur für Skalare. Hier ist ein Unterprogramm, das sich seine Argumente merkt und eine laufende Summe liefert, indem es ein state Array verwendet:

use v5.10;

running_sum( 5, 6 );
running_sum( 1..3 );
running_sum( 4 );

sub running_sum {
  state $sum = 0;
  state @numbers;

  foreach my $number ( @_ ) {
    push @numbers, $number;
    $sum += $number;
  }

  say "The sum of (@numbers) is $sum";
}

Diese gibt bei jedem Aufruf eine neue Summe aus, wobei die neuen Argumente zu allen vorherigen addiert werden:

The sum of (5 6) is 11
The sum of (5 6 1 2 3) is 17
The sum of (5 6 1 2 3 4) is 21

Es gibt allerdings eine kleine Einschränkung für Arrays und Hashes als state Variablen. Ab Perl 5.10 kannst du sie nicht mehr in Listenkontexten initialisieren:

state @array = qw(a b c); # Error!

Du bekommst eine Fehlermeldung, die darauf hinweist, dass du das vielleicht in einer zukünftigen Version von Perl machen kannst, aber ab Perl 5.24 geht das noch nicht:

Initialization of state variables in list context currently forbidden ...

Diese Einschränkung ist in Perl 5.28 aufgehoben, so dass du Arrays und Hashes in state initialisieren kannst. Ein Fibonacci-Zahlengenerator braucht zum Beispiel die ersten beiden Zahlen, um anzufangen, also startest du das Array @numbers mit diesen Zahlen:

use v5.28;

say next_fibonacci(); # 1
say next_fibonacci(); # 2
say next_fibonacci(); # 3
say next_fibonacci(); # 5

sub next_fibonacci {
  state @numbers = ( 0, 1 );
  push @numbers, $numbers[-2] + $numbers[-1];
  return $numbers[-1];
}

Vor Version 5.28 hättest du stattdessen eine Array-Referenz verwenden können, da alle Referenzen Skalare sind. Wir erklären dir das aber erst in Intermediate Perl.

Unterroutine Signaturen

Perl v5.20 hat eine lang erwartete und aufregende Funktion namens Subroutinen-Signaturen hinzugefügt. Bislang ist sie noch experimentell (siehe Anhang D), aber wir hoffen, dass sie bald stabil ist. Wir sind der Meinung, dass sie einen eigenen Abschnitt in diesem Kapitel verdient, auch wenn wir sie dir nur kurz vorstellen wollen.

Unterprogramm-Signaturen unterscheiden sich von Prototypen, einer ganz anderen Funktion, die viele Leute aus demselben Grund zu nutzen versucht haben. Wenn du nicht weißt, was ein Prototyp ist, ist das gut. Du brauchst sie wahrscheinlich nicht zu kennen, zumindest nicht in diesem Buch.

Bisher hast du Unterprogramme gesehen, die eine Liste von Argumenten in @_ entgegennehmen und sie dann Variablen zuweisen. Das hast du bereits in dem Unterprogramm maxgesehen:

sub max {
  my($m, $n);
  ($m, $n) = @_;
  if ($m > $n) { $m } else { $n }
}

Zuerst musst du diese experimentelle Funktion aktivieren (siehe Anhang D):

use v5.20;
use experimental qw(signatures);

Danach können wir die Variablendeklarationen außerhalb der geschweiften Klammern direkt nach den Namen der Unterroutinen verschieben:

sub max ( $m, $n ) {
  if ($m > $n) { $m } else { $n }
}

Das ist eine äußerst angenehme Syntax. Die Variablen sind immer noch privat für das Unterprogramm, aber du musst viel weniger eingeben, um sie zu deklarieren und zuzuweisen. Das erledigt Perl für dich. Ansonsten ist die Subroutine dieselbe.

Nun, es ist fast dasselbe. Früher konntest du &max beliebig viele Argumente übergeben, auch wenn du nur die ersten beiden davon verwendet hast. Das funktioniert jetzt nicht mehr:

&max( 137, 48, 7 );

Du bekommst eine Fehlermeldung, wenn du das versuchst:

Too many arguments for subroutine

Die Signaturfunktion prüft auch die Anzahl der Argumente für dich! Du willst aber das Maximum einer Liste von Zahlen ermitteln, deren Länge du nicht kennst. Du kannst die Unterroutine auf dieselbe Weise ändern, wie du sie zuvor geändert hast. Du kannst ein Array in der Signatur verwenden:

sub max ( $max_so_far, @rest ) {
  foreach (@rest) {
    if ($_ > $max_so_far) {
      $max_so_far = $_;
    }
  }
  $max_so_far;
}

Du musst aber kein Array definieren, das die restlichen Argumente aufnimmt. Wenn du ein einfaches @ verwendest, weiß Perl, dass die Unterroutine eine variable Anzahl von Argumenten annehmen kann. Die Argumentliste wird trotzdem in @_ angezeigt:

sub max ( $max_so_far, @ ) {
  foreach (@_) {
    if ($_ > $max_so_far) {
      $max_so_far = $_;
    }
  }
  $max_so_far;
}

Damit ist der Fall von zu vielen Argumenten gelöst, aber was ist mit zu wenigen Argumenten? Signaturen können auch Standardwerte angeben:

sub list_from_fred_to_barney ( $fred = 0, $barney = 7 ) {
  if ($fred < $barney) { $fred..$barney }
  else                 { reverse $barney..$fred }
}


my @defaults    = list_from_fred_to_barney();
my @default_end = list_from_fred_to_barney( 17 );

say "defaults: @defaults";
say "default_end: @default_end";

Wenn du dies ausführst, kannst du die Standardwerte bei der Arbeit sehen:

defaults: 0 1 2 3 4 5 6 7
default_end: 17 16 15 14 13 12 11 10 9 8 7

Manchmal brauchst du optionale Argumente, für die es keine Standardwerte gibt. Du kannst den Platzhalter $= verwenden, um ein optionales Argument zu kennzeichnen:

sub one_or_two_args ( $first, $= ) { ... }

Es gibt eine spezielle Perl-Variable für Formate, $=, aber darum geht es hier nicht.

Und manchmal willst du genau null Argumente. Du könntest einen konstanten Wert wie diesen erstellen:

sub PI () { 3.1415926 }

Du kannst mehr über Signaturen in perlsub lesen. Wir schreiben auch über sie im Blogbeitrag "Unterprogrammsignaturen in v5.20 verwenden".

Prototypen

Prototypen sind ein älteres Perl-Feature, mit dem du dem Parser mitteilen kannst, wie dein Quelltext zu interpretieren ist; sie sind eine primitive Form von Signaturen, die sich nie weiterentwickelt haben. Wir empfehlen diese Funktion nicht, aber sie kollidiert mit Signaturen, deshalb wollen wir sie nur erwähnen, damit du weißt, dass sie existiert.

Angenommen, du willst ein Unterprogramm, das genau zwei Argumente annimmt. Das könntest du im Prototyp vermerken. Da es sich dabei um Anweisungen an den Parser handelt, muss der Prototyp vor dem Aufruf der Unterroutine angezeigt werden:

sub sum ($$) { $_[0] + $_[1] }

print sum( 1, 3, 7 );

Dieser Code gibt dir einen Kompilierungsfehler, weil sum ein Argument mehr hat, als es sollte:

Too many arguments for main::sum

Prototypen und Signaturen verwenden beide Klammern nach dem Namen in einer Unterprogrammdefinition, und jede hat ihre eigene Syntax. Das ist ein Problem, wenn du beides verwenden willst. Um dieses Problem zu umgehen, wurde in Version 5.20 das Attribut :prototype eingeführt, eine weitere Funktion, die wir hier nicht näher erläutern, sondern nur darauf hinweisen, wie du Prototypen beibehalten und gleichzeitig Signaturen verwenden kannst. Setze :prototype vor die Klammern:

sub sum :prototype($$) { $_[0] + $_[1] }

print sum( 1, 3, 7 );

Wir raten dir, Prototypen gänzlich zu vermeiden, es sei denn, du weißt, was du tust. Selbst dann solltest du es dir zweimal überlegen. Die vollständigen Details findest du in perlsub. Wenn du keine Ahnung hast, wovon wir reden, ist das kein Problem, denn du wirst das in diesem Buch nicht wieder sehen!

Übungen

Siehe "Antworten zu den Übungen in Kapitel 4" für die Antworten auf diese Übungen:

  1. [12] Schreibe ein Unterprogramm mit dem Namen total, das die Summe einer Liste von Zahlen zurückgibt. Tipp: Das Unterprogramm sollte keine E/A durchführen, sondern nur seine Parameter verarbeiten und einen Wert an seinen Aufrufer zurückgeben. Probiere es in diesem Beispielprogramm aus, das die Unterroutine nur ausprobiert, um zu sehen, ob sie funktioniert. Die erste Zahlengruppe sollte die Summe 25 ergeben.

    my @fred = qw{ 1 3 5 7 9 };
    my $fred_total = total(@fred);
    print "The total of \@fred is $fred_total.\n";
    print "Enter some numbers on separate lines: ";
    my $user_total = total(<STDIN>);
    print "The total of those numbers is $user_total.\n";

    Beachte, dass die Verwendung von <STDIN> in einem solchen Listenkontext darauf wartet, dass du die Eingabe auf die für dein System geeignete Weise beendest.

  2. [5] Erstelle mit Hilfe des Unterprogramms aus der vorherigen Aufgabe ein Programm, das die Summe der Zahlen von 1 bis 1.000 berechnet.

  3. [18] Zusatzaufgabe: Schreibe ein Unterprogramm mit dem Namen &above_average, das aus einer Liste von Zahlen die Zahlen zurückgibt, die über dem Durchschnitt (Mittelwert) liegen. (Tipp: Erstelle ein weiteres Unterprogramm, das den Durchschnitt berechnet, indem es die Gesamtsumme durch die Anzahl der Posten teilt). Probiere deine Subroutine in diesem Testprogramm aus:

    my @fred = above_average(1..10);
    print "\@fred is @fred\n";
    print "(Should be 6 7 8 9 10)\n";
    my @barney = above_average(100, 1..10);
    print "\@barney is @barney\n";
    print "(Should be just 100)\n";
  4. [10] Schreibe ein Unterprogramm namens greet, das die von dir genannte Person begrüßt, indem es ihr den Namen der letzten Person mitteilt, die es begrüßt hat:

    greet( "Fred" );
    greet( "Barney" );

    Diese Folge von Anweisungen sollte gedruckt werden:

    Hi Fred! You are the first one here!
    Hi Barney! Fred is also here!
  5. [10] Ändere das vorherige Programm so ab, dass es jeder neuen Person die Namen aller Personen mitteilt, die es zuvor begrüßt hat:

    greet( "Fred" );
    greet( "Barney" );
    greet( "Wilma" );
    greet( "Betty" );

    Diese Folge von Anweisungen sollte gedruckt werden:

    Hi Fred! You are the first one here!
    Hi Barney! I've seen: Fred
    Hi Wilma! I've seen: Fred Barney
    Hi Betty! I've seen: Fred Barney Wilma

Get Perl lernen, 8. Auflage now with the O’Reilly learning platform.

O’Reilly members experience books, live events, courses curated by job role, and more from O’Reilly and nearly 200 top publishers.