




Wir vervollständigen Ihr Wissen über den Perl-Sprachkern heute mit dem Thema Subroutinen, Funktionen und lokale Variablen. Wenn Sie bestimmten Code immer wieder verwenden, müssen Sie ihn nicht jedesmal in Ihr Skript tippen - Sie können ihn in eine Subroutine packen und diese, wann immer Sie sie brauchen, aufrufen - genau wie Sie es von Perl-Funktionen kennen.
Heute erkläre ich Ihnen unter anderem:
Eine Funktion ist im allgemeinen ein Stück Code, das auf irgendwelchen Daten einen oder mehrere Vorgänge ausführt. Man ruft eine Funktion auf, indem man im Skript ihren Namen nennt und ihr Argumente übergibt. Wenn Perl das Skript ausführt und auf einen Funktionsaufruf stößt, wechselt Perl zur entsprechenden Funktionsdefinition, führt die dortigen Anweisungen aus und kehrt dann an die Stelle zurück, an der es das Skript verlassen hat. Das gilt für alle Arten von Funktionen.
  Wir haben in den bisherigen Kapiteln bereits viel mit Perls eigenen Funktionen wie 
print, sort, keys, chomp etc. gearbeitet. Diese sind in Perls Standardbibliothek 
definiert, so dass sie in allen Perl-Programmen zur Verfügung stehen. Eine zweite Art 
von Funktionen sind in zusätzlichen Perl-Modulen oder -Bibliotheken definiert. Um 
eine solche Funktion aufrufen zu können, müssen Sie am Anfang Ihres Skripts das 
zugehörige Modul geladen haben - wie das geht, zeige ich Ihnen am Tag 13.
Eine dritte Art von Funktionen sind die Subroutinen, die Sie selbst definieren. Die Begriffe Funktion und Subroutine bedeuten in Perl im Grunde das gleiche.1 Manche Programmierer nennen Subroutinen auch benutzerdefininierte Funktionen, um sie von Perls eigenen Funktionen zu unterscheiden, in anderen Zusammenhängen ist eine Unterscheidung gar nicht nötig. Ich nenne in diesem Buch die Funktionen, die Sie in Ihren eigenen Programmen (und später auch Modulen) selbst definieren, Subroutinen. Funktionen nenne ich die, die Sie woanders herbekommen - aus der Perl- Standardbibliothek oder optionalen Modulen.
  Was nützt Ihnen eine Subroutine? Sobald Sie mehr als nur ein paar Zeilen Code in 
Ihren Skripts wiederholen müssen, haben Sie einen guten Grund, diesen Code in eine 
Subroutine zu packen. Das erspart Ihnen Tipparbeit. Doch auch wenn Sie den Code 
nur einmal brauchen, kann es - besonders bei komplexen Aufgaben - von Nutzen 
sein, das Skript aufzuteilen und die verschiedenen Teilaufgaben in eigenen 
Subroutinen zu lösen. Klare Namen für die einzelnen Vorgänge (diagramm_ausgeben 
oder max_finden) steigern die Übersichtlichkeit Ihres Skripts. Eventuelle Probleme 
lassen sich schneller isolieren: Sie können jede Subroutine für sich allein schreiben, 
testen und überprüfen - und sicher sein, dass sie, wenn Sie sie schließlich in Ihr 
Gesamtskript einfügen, auch funktioniert, wie Sie erwarten. Subroutinen sind eine 
Frage des Programmierstils - je mehr Einzelprobleme Sie auch in eigenen 
Subroutinen lösen, desto übersichtlicher und nachvollziehbarer werden Ihre Skripts für 
Sie und andere.
Die einfachste Form von Subroutine nimmt keine Argumente entgegen, verwendet keine lokalen Variablen, gibt keinen Wert zurück - und hat nicht besonders viel Sinn. Am Beispiel einer solchen Subroutine zeige ich Ihnen im folgenden Abschnitt, wie Sie Subroutinen deklarieren, definieren und aufrufen.
  Nehmen wir ein ganz einfaches Beispiel. Erinnern Sie sich an das Skript 
temperatur.pl von Tag 2, das nach einer Temperatur in Fahrenheit gefragt und sie in 
Celsius umgerechnet hat? Die eigentliche Berechnung stand mitten im Skript, aber 
wir hätten sie auch in eine Subroutine stellen können:
1#!/usr/bin/perl -w
$fahr = 0;
$cel = 0;
print 'Geben Sie eine Temperatur in Fahrenheit ein ';
chomp ($fahr = <STDIN>);
&f2c(); #Fahrenheit zu Celsius
print "$fahr Grad Fahrenheit entsprechen ";
printf("%d Grad Celsius\n", $cel);
sub f2c {
$cel = ($fahr - 32) * 5 / 9;
}
  Sehen Sie sich genau an, wie wir die Subroutine hier einsetzen. Perl führt das Skript 
wie gewohnt Zeile für Zeile aus, bis es zu dem Verweis auf eine Subroutine (hier &f2c) 
gelangt. Jetzt wechselt Perl zur Subroutinendefinition (den letzten Zeilen im Skript), 
führt den Block der Subroutine aus und kehrt dann dorthin zurück, wo die normale 
Ausführung unterbrochen wurde. In unserem Beispiel bedeutet dies, dass Perl, 
nachdem es von der Tastatur die Temperatur eingelesen hat, zur Subroutine &f2c 
wechselt, den Wert in Celsius umrechnet und dann das Ergebnis ausgibt.

Wenn ich sage, Perl wechselt zur Subroutine, meine ich nicht, dass Perl wirklich zu dieser Skriptzeile springt. Bevor Perl ein Skript ausführt, liest es das gesamte Skript in den Speicher und informiert sich über die Definitionen der Subroutinen. Später springt die Ausführung dann nur noch zu den Definitionen der Subroutinen und wieder zurück.
An diesem simplen Beispiel haben Sie gesehen, wie man Subroutinen definiert:
sub subroutinenname {
   Anweisungen;
   ...
}
  Eine Subroutinendefinition beginnt mit dem Wort sub, gefolgt vom Namen der 
Subroutine, gefolgt von einem Block. Der Block ist, wie Sie von Schleifen und 
Bedingungen her kennen, eine mit geschweiften Klammern umgebene Folge von 
Perl-Anweisungen. Ein Beispiel:
sub zahl_holen {
   print 'Bitte eine Zahl eingeben: ';
   chomp($zahl = <STDIN>);
}
Der Name einer Subroutine kann aus beliebig vielen alphanumerischen Zeichen und Unterstrichen bestehen, kommt nicht in Konflikt mit gleichnamigen Skalar-, Array- oder Hash-Variablen und unterscheidet zwischen Groß- und Kleinbuchstaben.
Subroutinendefinitionen können Sie überall dorthin stellen, wo auch eine normale Anweisung stehen kann - an den Anfang Ihres Skripts, ans Ende oder mitten hinein (sogar in andere Blöcke). Im allgemeinen empfiehlt es sich jedoch aus Gründen der Übersichtlichkeit, sie alle zusammen an den Anfang oder das Ende des Skripts zu setzen.
  Mit einem kaufmännischen Und (&) vor dem Namen der Subroutine und optionalen 
runden Klammern danach rufen Sie eine Subroutine auf:
&f2c; #Fahrenheit zu Celsius
&zahl_holen()
In die Klammern kommen die Argumente, die Sie der Subroutine übergeben (mehr dazu später).
  Das kaufmännische Und (&) ist optional; Sie können Subroutinen auch ohne & 
aufrufen. Einige Programmierer verwenden das &, weil es die Unterscheidung von 
Perl-eigenen Funktionen und selbst definierten (oder aus Modulen importierten) 
Subroutinen erleichtert. Ich rufe in diesem Buch Subroutinen immer mit & auf.

Es gibt einen Ausnahmefall, in dem das kaufmännische Und nicht optional ist: Wenn Sie sich indirekt auf eine Subroutine beziehen, sie aber nicht aufrufen, also zum Beispiel mit
definedüberprüfen, ob die Subroutine definiert ist, dann müssen Sie das&verwenden.Außerdem könnten Sie in einigen Fällen auch die Klammern weglassen (insbesondere, wenn die Subroutine an einer früheren Stelle im Skript oder importierten Modul bereits deklariert wurde). Über die Deklaration von Subroutinen erfahren Sie im Vertiefungsabschnitt mehr. Ich verwende die Klammern in jedem Fall, um Verwirrung zu vermeiden - denn verkehrt sind sie nie.
Sie können eine Subroutine nicht nur aus dem Hauptkörper Ihres Skripts aufrufen, sondern auch aus anderen Subroutinen - die Sie vielleicht aus wieder anderen Subroutinen aufgerufen haben. In Perl können Sie Subroutinenaufrufe so tief verschachteln, wie Ihr Speicher erlaubt. Auf ein paar Dinge wie die Argumente oder die Gültigkeitsbereiche der lokalen Variablen müssen Sie dabei noch achten, aber dazu kommen wir noch.
Mehr oder weniger zum Spaß habe ich mir die letzte Version unseres Statistikskripts geschnappt, in seine Einzelteile zerlegt und jedes in eine Subroutine gepackt. Im Hauptkörper des Skripts werden jetzt nur noch Subroutinen aufgerufen. Am Verhalten des Skripts hat sich nichts geändert, nur an seinem Aufbau. Das Ergebnis können Sie in Listing 11.1 bewundern.
Listing 11.1: Das Skript statsfunk.pl
1: #!/usr/bin/perl -w
2:
3: &initvars();
4: &getinput();
5: &printresults();
6:
7: sub initvars {
8: $input = ""; # temp Input
9: @nums = (); # Array: Zahlen
10: %freq = (); # Hash: Zahl-Haeufigkeit
11: $maxfreq = 0; # hoechste Haeufigkeit
12: $count = 0; # Anzahl Zahlen
13: $sum = 0; # Summe
14: $avg = 0; # Durchschnitt
15: $med = 0; # Median
16: @keys = (); # temp keys
17: $totalspace = 0; # gesamte Breite des Histogramms
18: }
19:
20: sub getinput {
21: while (defined ($input = <>)) {
22: chomp ($input);
23: $nums[$count] = $input;
24: $freq{$input}++;
25: if ($maxfreq < $freq{$input}) { $maxfreq = $freq{$input} }
26: $count++;
27: $sum += $input;
28: }
29:
30: }
31:
32: sub printresults {
33: @nums = sort { $a <=> $b } @nums;
34:
35: $avg = $sum / $count;
36: $med = $nums[$count /2];
37:
38 print "\nAnzahl der eingegebenen Zahlen: $count\n";
39: print "Summe der Zahlen: $sum\n";
40: print "Kleinste Zahl: $nums[0]\n";
41: print "Groesste Zahl: $nums[$#nums]\n";
42: printf("Durchschnitt: %.2f\n", $avg);
43: print "Mittelwert: $med\n\n";
44: &printhist();
45: }
46:
47: sub printhist {
48: @keys = sort { $a <=> $b } keys %freq;
49:
50: for ($i = $maxfreq; $i > 0; $i--) {
51: foreach $num (@keys) {
52: $space = (length $num);
53: if ($freq{$num} >= $i) {
54: print( (" " x $space) . "*");
55: } else {
56: print " " x (($space) + 1);
57: }
58: if ($i == $maxfreq) { $totalspace += $space + 1; }
59: }
60: print "\n";
61: }
62: print "-" x $totalspace;
63: print "\n @keys\n";
64: }
Da diese Version des Skripts eigentlich nichts anderes macht als die letzte, gibt es hier nur ein paar Dinge anzumerken:
printresults ruft am Ende des Blocks die 
Subroutine printhist auf.
   
Mit einem Aufruf der Subroutine allein, als einziger Anweisung in einer Zeile, wird die Subroutine ausgeführt - und das war's dann auch schon. Viel nützlicher sind Subroutinen, die einen Wert zurückgeben: Sie können sie in Ausdrücke oder Anweisungen einbauen oder als Argument an andere Subroutinen übergeben.
Wir haben schon besprochen, dass ein Block das Ergebnis der letzten Auswertung zurückgibt. Der Block, der eine Subroutine definiert, macht da keinen Unterschied. So liest zum Beispiel das folgende Skript zwei Zahlen ein und addiert sie:
$sum = &sumnums();
print "Summe: $sum\n";
sub sumnums { # Zahlen addieren
print 'Geben Sie eine Zahl ein: ';
chomp($zahl1 = <STDIN>);
print 'Geben Sie eine weiter Zahl ein: ';
chomp($zahl2 = <STDIN>);
$zahl1 + $zahl2;
}
  In der ersten Zeile haben wir hier einen Subroutinenaufruf auf der rechten Seite einer 
Zuweisung. Was wir hier zuweisen ist nicht etwa die Subroutine selbst (wie sollte das 
aussehen?), sondern ihren Rückgabewert. Das Ergebnis der Addition ($zahl1 + 
$zahl2) in der letzten Zeile der Subroutine &sumnums ist der Wert, den die Subroutine 
zurückgibt, der an $sum zugewiesen und von print ausgegeben wird.
  Der Haken an diesem Verhalten ist, dass die letzte Anweisung im Block zwar häufig, 
aber nicht immer den Wert liefert, den die Subroutine zurückgeben soll. Die Regel 
besagt, dass ein Block das Ergebnis der letzten Auswertung zurückgibt - und was 
zuletzt ausgewertet wird, muss nicht immer die letzte Anweisung; es könnte zum 
Beispiel auch die Schleifenbedingung einer while- oder for-Schleife oder ein 
Schleifensteuerbefehl sein.
  Weil der Rückgabewert einer Subroutine nicht immer klar ist, empfiehlt es sich, in der 
letzten Zeile mit return (zurückgeben) explizit festzulegen, was sie zurückgeben soll. 
Die Funktion return nimmt einen Ausdruck als Argument entgegen, wertet ihn aus 
und gibt das Ergebnis zurück. Die letzte Zeile von &sumnums sähe dann so aus:
return $zahl1 + $zahl2;
  Sie möchten mehrere Werte aus einer Subroutine zurückgeben? Kein Problem. 
Geben Sie einfach eine Liste zurück (aber achten Sie im Hauptteil Ihres Skripts darauf, 
dass Sie mit dieser Liste richtig umgehen). Das folgende Code-Fragment zum Beispiel 
ruft eine Subroutine auf, die die Werte aus einem Array verarbeitet und eine Liste von 
drei Werten zurückgibt, die dann den Elementen $max, $min und $count zugewiesen 
werden (wie wir der Subroutine das Array @zahlen übergeben, soll uns hier noch nicht 
interessieren):
($max, $min, $count) = &berechne(@zahlen);
# ...
sub berechne {
# ...
return ($wert1, $wert2, $wert3);
}
  Und wenn Sie mehrere Listen aus einer Subroutine zurückgeben möchten? Das geht 
nicht oder zumindest nicht so einfach. Die Funktion return löst in einem 
Listenargument alle eventuellen Unterlisten und Hashes in ihre Elemente auf - sie 
interpoliert Arrays und Hashes in eine lineare Liste von Skalaren. Dieses Verhalten ist 
keine Besonderheit von return, sondern Perls allgemeine »Datenliefermethode« von 
und an Subroutinen (mehr dazu später). Brauchen Sie von einer Subroutine wirklich 
mehrere Listen, müssen Sie dafür sorgen, dass sich die gelieferte lineare Liste 
außerhalb der Subroutine wieder in die Teillisten zurückverwandeln läßt.
Ein Perl-Skript in Subroutinen aufzuteilen, die mit denselben globalen Variablen arbeiten, ist im Grunde eine reine Codeformatierungsübung. Vom besseren Überblick einmal abgesehen, bringen Ihnen solche Subroutinen keine wesentlichen Vorteile. Unser Ziel jedoch sind Subroutinen als eigenständige Einheiten, die eigene Variablen haben, nur die ihnen übergebenen Argumenten bearbeiteten und die angeforderten Ergebnisse liefern. Solche Subroutinen sind leichter zu handhaben, wiederzuverwenden und zu debuggen. Diesem Ziel werden wir uns jetzt Schritt für Schritt annähern; und wir beginnen mit den lokalen Variablen.
  Bisher hatten wir fast ausschließlich mit globalen Variablen zu tun. Global bedeutet, 
dass diese (Skalar-, Array- und Hash-) Variablen überall im Skript zur Verfügung 
stehen und so lange existieren, wie das Skript läuft. Sie haben zwar bereits ein paar 
Ausnahmen gesehen - zum Beispiel die Schleifenvariable von foreach oder die 
Treffervariablen in regulären Ausdrücken - doch größtenteils gehörten unsere 
Variablen dem globalen Gültigkeitsbereich an.
Lokale Variablen sind, in Verbindung mit Subroutinen, Variablen, die erst beim Start der Subroutine zu existieren beginnen, nur in der Subroutine zur Verfügung stehen und wieder verschwinden, sobald die Subroutine beendet ist. Ansonsten sind es ganz normale Variablen - sie sehen weder anders aus, noch werden sie anders verwendet als ihre globalen Pendants.
Sie haben in Perl zwei Möglichkeiten, lokale Variablen zu erstellen. Die einfachere behandeln wir hier, die andere (und worin sie sich unterscheidet) am Tag 13.
  Zum Deklarieren einer lokalen Variablen für eine Subroutine setzen Sie den Operator 
my vor die Variable, wenn Sie sie initialisieren oder das erste Mal verwenden:
my $x = 1; # $x ist jetzt lokal
  Wenn Sie mehrere lokale Variablen deklarieren möchten, müssen Sie die Liste in 
Klammern setzen, sonst wirkt sich my nur auf die erste aus:2
my ($a, $b, $c); # drei lokale Variablen, alle undefiniert.
  Im vorigen Abschnitt haben wir eine Subroutine erstellt, die zwei Zahlen erfragt und 
addiert hat. Die beiden Variablen $zahl1 und $zahl2 waren global. Mit einer 
zusätzlichen Zeile machen wir sie zu lokalen Variablen:
$sum = &sumnums();
print "Summe: $sum\n";
sub sumnums {
my ($zahl1, $zahl2);
print 'Geben Sie eine Zahl ein: ';
chomp($zahl1 = <STDIN>);
print 'Geben Sie eine weiter Zahl ein: ';
chomp($zahl2 = <STDIN>);
return ($zahl1 + $zahl2);
}
In dieser Version deklariert die Zeile my ($zahl1, $zahl2) zwei lokale Variablen. Diese sind nur innerhalb der Subroutine sumnums existent und sichtbar.
  Was passiert, wenn vor dem Subroutinenaufruf schon eine globale Variable $zahl1 
vorhanden war? Perl hat damit kein Problem. Eine mit my deklarierte Variable 
verdeckt eine globale Variable gleichen Namens vor »ihrer Subroutine«. Oder 
andersherum formuliert: In einer Subroutine ist eine globale Variable unsichtbar, 
sobald in der Subroutine eine gleichnamige lokale Variable deklariert wird. Wenn Perl 
die Subroutine verläßt, verschwindet die my-Variable wieder, und Sie haben einen 
freien Blick (und Zugriff) auf die globale Variable. Weil dies - vor allem bei der 
Fehlersuche - sehr verwirren kann, ist es im allgemeinen eine gute Idee, Ihre lokalen 
Variablen anders zu nennen als Ihre globalen.

Über den Paketnamen kommen Sie in einer Subroutine aber auch an versteckte globale Variablen heran. Wir besprechen das an Tag 13.
Betrachten wir folgendes Beispiel:
#!/usr/bin/perl -w
$wert = 0;
print "vor subaufruf - \$wert ist $wert\n";
&subaufruf();
print "nach subaufruf - \$wert ist $wert\n";
sub subaufruf {
my ($wert);
$wert = 10;
print "innerhalb von subaufruf - \$wert ist $wert\n";
}
Dieses Skriptchen produziert folgende Ausgabe:
vor subaufruf - $wert ist 0
innerhalb von subaufruf - $wert ist 10
nach subaufruf - $wert ist 0
  Sie sehen es selbst: Die lokale Variable $wert, die in der Subroutine deklariert wird, 
hat mit der globalen nichts zu tun. Sie in der Subroutine zu verändern, wirkt sich nicht 
im geringsten auf die globale Namensschwester aus.
  Wenn wir jetzt aus subaufruf eine weitere Subroutine namens subaufruf2 aufrufen 
und dort wieder eine my-Variable $wert mit 0 initialisieren und inkrementieren 
würden, hätten wir drei völlig verschiedene Variablen namens $wert und folgende 
Ausgabe:
vor subaufruf - $wert ist 0
innerhalb von subaufruf - $wert ist 1
innerhalb von subaufruf2 - $wert ist 1
nach subaufruf - $wert ist 0
  Auch wenn Sie Subroutinen ineinander verschachteln - eine Subroutine aus einer 
anderen Subroutine aufrufen -, sind die in der aufrufenden Subroutine deklarierten my-
Variablen in der aufgerufenen Subroutine unsichtbar und umgekehrt.3 Hier 
unterscheidet sich Perl von vielen anderen Sprachen, die die Gültigkeitsbereiche von 
lokalen Variablen in verschachtelten Subroutinen »kaskadieren«. my-Variablen 
existieren in Perl wirklich einzig und allein in der Subroutine, in der sie deklariert 
werden, nirgendwo sonst.
  Für die Computerexperten unter den Lesern heißt dies, dass die my-Variablen 
lexikalische statt dynamische Gültigkeit haben. Sie existieren nur innerhalb des 
Blocks, in dem sie definiert sind. Dazu gleich mehr.
Jetzt wissen Sie, wie Subroutinen Werte zurückliefern und Daten in lokalen Variablen speichern. Fehlt nur noch, wie man ihnen Werte übergibt. Damit kommen wir auf die Argumente zu sprechen.
Das Konzept der Datenübergabe an eine Subroutine (und zurück) ist in Perl ebenso schlicht wie ergreifend. Während Sie in anderen Sprachen schon beim Schreiben einer Subroutine genau festlegen müssen, wie viele Argumente welchen Typs sie entgegennehmen kann, packt bzw. expandiert Perl einfach alle Ihre Argumente in eine einzige lineare Liste. Bei skalaren Argumenten ist das keine besondere Aktion:
&meine_subroutine(1, 2, 3);
  &meine_subroutine erhält in diesem Fall eine Liste von drei numerischen Werten. 
Achten Sie aber auf Listenargumente:
&meine_subroutine(@liste, @andere_liste);
In diesem Fall expandiert Perl die beiden Arrayvariablen und speichert alle ihre Elemente nacheinander in einer einzigen Liste - der Liste, die es an die Subroutine übergibt. Die Identität der Arrays geht in dieser Liste verloren (die Inhalte sind zwar noch alle da, aber wenn Sie nicht vorgesorgt haben, läßt sich nicht mehr herausfinden, aus welchem Array sie stammen).
Bei Hashes ist es ähnlich: Ein Hash wird (wie immer, wenn Sie ihn einer Liste zuweisen) in seine Bestandteile zerlegt. Seine Schlüssel und Werte werden der Liste hinzugefügt, die die Subroutine schließlich entgegennimmt.
Doch was, wenn Ihre Argumente wirklich aus mehreren Arrays oder Hashes bestehen sollen? Keine Panik, es gibt ein paar Möglichkeiten, Perls Datenübergabeverhalten zu umgehen. Eine Möglichkeit wäre, Ihre Arrays oder Hashes als globale Variablen zu speichern und sich in der Subroutine einfach auf sie zu beziehen. Mit ein paar vorbereitenden Tricks könnten Sie die Teillisten auch aus der Argumentliste rekonstruieren (zum Beispiel, indem Sie als Argument auch die Länge der Arrays mit übergeben). Die dritte und wahrscheinlich die beste Möglichkeit ist, die Argumente als Referenzen zu übergeben. Die Struktur der Arrays und Hashes bliebe dabei erhalten - mit Referenzen befassen wir uns an Tag 19.
Okay, die Argumente wurden in einer linearen Liste an die Subroutine übergeben. Und nun? Wie kommen Sie vom Körper der Subroutine aus an diese Parameterliste heran?4
  Die an eine Subroutine übergebene Liste (auch Parameterliste dieser Subroutine 
genannt) wird im lokalen Spezialarray @_ gespeichert. Auf dieses Array können Sie 
mit den bekannten Techniken zugreifen. @_ ist eine lokale Variable der Subroutine, 
das heißt ihr Inhalt ist nur innerhalb der Subroutine sichtbar. Wenn Sie aus einer 
Subroutine heraus eine andere aufrufen, erhält diese zweite Subroutine ihr eigenes @_-
Array.
Hier ein Beispiel einer Subroutine, die zwei Argumente addiert:
&addiere(2,3); #Aufruf der Subroutine
sub addiere {
return $_[0] + $_[1];
}
  Die beiden Argumente an die Subroutine - hier 2 und 3 - landen im Array @_. Mit den 
Ausdrücken $_[0] und $_[1] greifen Sie auf die ersten beiden Elemente dieses Arrays 
zu. Genau wie $foo[0] und $foo sich auf unterschiedliche Dinge beziehen, ist auch 
$_[0] etwas anderes als $_ ($_[0] ist das erste Element in der Argumentliste der 
Subroutine, $_ ist die Default-String-Spezialvariable).
  Diese Subroutine ist nicht gerade intelligent - sie addiert lediglich die ersten beiden 
Argumente, ganz egal, wie viele Sie ihr übergeben haben. Weil Sie nicht kontrollieren 
können, wie viele Argumente eine Subroutine entgegennehmen kann, müßten Sie 
beim Aufruf auf die korrekte Anzahl achten oder in der Subroutine die Zahl der 
Argumente (also der Elemente in @_ ) überprüfen oder die Subroutine so umschreiben, 
dass sie etwas großzügiger mit mehreren Argumenten umgeht. Sie könnten sie zum 
Beispiel alle Argumente addieren lassen, wie viele es auch sein mögen:
sub addiere {
   my $sum = 0;
   foreach $i (@_) {
      $sum += $i;
   }

In den neueren Perl-Versionen (seit Release 5.003) haben Sie die Möglichkeit, bei der Deklaration einer Subroutine mit sogenannten Prototypen auch Art und Anzahl der Argumente zu bestimmen. Da dieses Feature relativ neu ist, möchten Sie sich in Ihren Skripts aber vielleicht nicht darauf verlassen. Mehr zu Prototypen auf Seite 332.
  Perl hat keine eigene Methode zum expliziten Benennen von eingehenden 
Argumenten, aber ein sehr gebräuchlicher »Trick« macht praktisch das gleiche: 
Weisen Sie als erstes die Argumente von @_ lokalen Variablen zu:
sub foo {
   my($max, $min, $inc) = @_;
   ...
}
  Dann können Sie sich auf die Argumente mit einem leichter zu merkenden 
Variablennamen beziehen und die Positionen der einzelnen Argumente im Array @_ 
getrost vergessen. Eine andere (sehr gebräuchliche) Möglichkeit führt zum gleichen 
Ergebnis: Erinnern Sie sich an die Funktion shift? Bequemerweise entfernt shift, 
wenn es ohne Argumente innerhalb einer Subroutine aufgerufen wird, das erste 
Element von @_ und gibt es zurück (pop macht dasselbe mit dem letzten Element von 
@_):
sub foo {
   my $max = shift;
   my $min = shift;
   my $inc = shift;
   ...
}
  Die Werte von @_ sind sogenannte Referenzparameter: Sie verweisen implizit auf die 
eigentlichen skalaren Argumente, die von außen übergeben wurden. Das bedeutet, 
dass, wenn Sie direkt im Array @_ ein Element verändern, unter Umständen auch 
einen Wert außerhalb der Subroutine verändern. Betrachten wir am besten, was 
folgendes Beispiel ausgibt:
#!/usr/bin/perl -w
$a = "a";
@b = ($a, 0);
%c = ('rom', '1', 'berlin', '2');
print "\n vor subaufruf: \$a = $a \@b = @b \%c = ";
foreach (keys %c) {
print "$_ : $c{$_} ";
}
&subaufruf($a, 10, @b, %c,);
print "\n nach subaufruf: \$a = $a \@b = @b \%c = ";
foreach (keys %c) {
print "$_ : $c{$_} ";
}
sub subaufruf {
my ($a, $b, $c, $d, $e ) = @_;
print "\n start subaufruf: \@ = @_ ";
$a = "my a";
$b = 20;
$c = "my b1";
$_[0] = "AAA";
$_[2] = B1;
$_[3] = B2;
$_[4] = "PARIS";
print "\n ende subaufruf: \$a = $a \$b = $b \$c = $c \n";
print " \@ = @_";
}
  Dieser Code produziert folgenden Output (ohne dass wir mit subaufruf etwas 
zurückgeben):
vor subaufruf: $a = a @b = a 0 %c = berlin : 2 rom : 1
start subaufruf: @ = a 10 a 0 berlin 2 rom 1
ende subaufruf: $a = my a $b = 20 $c = my b1
@ = AAA 10 B1 B2 PARIS 2 rom 1
nach subaufruf: $a = AAA @b = B1 B2 %c = berlin : 2 rom : 1
  Hier sehen Sie, wie die globalen Variablen durch die Zuweisungen an $_[0] etc. 
verändert werden. Es ist, als stünden die Variablen selbst in der Parameterliste @_ - so 
etwas nennt man dann Referenzübergabe. Nur auf den Hash haben wir keinen Einfluß 
($_[4] = "PARIS" wirkt sich nicht auf %c aus), wir erhalten lediglich die aktuellen 
Werte seiner Elemente (ja, die Werte der Schlüssel und die Werte der Werte). So 
etwas nennt man dann Wertübergabe.
  Am Anfang von subaufruf weisen wir @_ einer my-Liste von lokalen Variablen zu.5 
Weil wir hier im Grunde eine Kopie der Parameterwerte machen, berühren wir keine 
der Originalvariablen, wenn wir die (Kopien in den) lokalen Variablen verändern - das 
ist die einfachere, verständlichere und empfehlenswerte Art des Umgangs mit unseren 
Argumenten.
Parameterübergabe, Subroutinendefinition, Rückgabewerte - all dies sind auch in anderen Sprachen wichtige Fragen bei der Definition von Funktionen. Aber das hier ist Perl; es muss noch ein paar Eigenar(tigkei)ten geben. Sie wissen bereits, dass Arrays und Hashes in einer Subroutinen-Parameterliste ihre Identität verlieren, weil sie aufgedröselt werden. Eine andere Eigenart von Perl ist der Kontext.
Da Subroutinen sowohl in Skalar- als auch in Listenkontext aufgerufen werden und sowohl einen Skalar als auch eine Liste zurückgeben können, spielt die Frage nach dem Kontext auch beim Definieren von Subroutinen eine Rolle - oder zumindest beim Aufrufen von Subroutinen: Rufen Sie Subroutinen, die Listen zurückgeben, nicht in Skalarkontext auf, wenn Sie nicht genau wissen, zu welchen Ergebnissen das führt.
  Vielleicht möchten Sie aber gerade eine Subroutine schreiben, die sich je nach 
Kontext, in dem sie aufgerufen wird, unterschiedlich verhält. Dann hat Perl eine nette 
Funktion für Sie: wantarray. Die Funktion wantarray gibt wahr zurück, wenn die 
momentan laufende Subroutine in Listenkontext aufgerufen wurde, falsch wenn in 
Skalarkontext (»wantlist«, »will Liste« wäre ein passenderer Name, aber aus 
historischen Gründen heißt sie eben wantarray). So können Sie zum Beispiel vom 
Kontext abhängig machen, welchen Wert Sie aus einer Subroutine zurückgeben:
sub skalar_oder_liste {
    # ...
    if (wantarray()) {
        return @liste;
    } else { return $skalar}
}
Gehen Sie mit diesem Feature aber vorsichtig um. Bedenken Sie, welche Verwirrung schon Perl-Funktionen stiften können, die sich je nach Kontext unterschiedlich verhalten (und so manchen Blick in die Dokumentation erfordern). In vielen Fällen ist es besser, einen der Subroutine selbst entsprechenden Wert zurückzugeben und mit diesem in der Anweisung, die die Subroutine aufgerufen hat, entsprechend umzugehen.
Verändern wir zum zweiten Mal in dieser Lektion unser gutes altes Statistikbeispiel und schöpfen wir dabei aus dem gesamten Wissen, das Sie im Laufe dieses Buches angehäuft haben. Diesmal soll das Skript nicht alles hintereinander weg ausführen, sondern die verschiedenen Operationen erst einmal in einem Menü anzeigen. Sie haben dann die Wahl, welche dieser Operationen Sie auf den aus Ihrer Input-Datei gelesenen Zahlen ausführen möchten.
  Anders als im Skript mehrnamen.pl vom Tag 8 steuern wir dieses Menü hier aber 
nicht über eine große while-Schleife und diversen Bedingungen, sondern über 
Subroutinen. Die verschiedenen Berechnungen verschieben wir in die Subroutinen, in 
denen sie gebraucht werden.
Weil dieses Skript ziemlich lang ist, zeige ich Ihnen diesmal nicht zuerst den kompletten Code und bespreche ihn dann hinterher, sondern ich fange mit den Erklärungen der einzelnen Schritte an und stelle das vollständige Listing ans Ende dieses Abschnitts.
Beginnen wir mit dem Hauptkörper des Skripts. In unseren bisherigen Versionen mussten wir erst einmal eine Menge Variablen initialisieren - globale Variablen. Diesmal ist unser Initialsierungsteil nur noch zwei Zeilen lang, denn wir brauchen nur zwei globale Variablen. Eine für den Input (die Zahlen, die wir aus der angegeben Datei lesen) und eine, mit der wir stets im Blick haben, ob das Skript beendet werden soll oder nicht. Alle anderen deklarieren wir in den Subroutinen als lokale Variablen:
#!/usr/bin/perl -w
@zahlen = (); #Input aus Datei, eine Zahl pro Zeile
$choice = "";
&getinput();
  Das Skript beginnt mit dem Aufruf der Subroutine &getinput. Diese Subroutine liest 
die Daten aus der Eingabedatei und speichert sie im Array @nums. Diese Version von 
getinput ist wesentlich kürzer als die letzte - wir kümmern uns hier nicht um die 
Häufigkeit der einzelnen Werte, ihre Gesamtsumme etc., und wir verwenden $_ 
anstatt einer temporären Input-Variablen. Das einzige, was wir außer dem Einlesen 
hier noch erledigen, ist das Sortieren der Zahlen im Array:
sub getinput {
    my $count = 0;
    while (<>) {
        chomp;
        $nums[$count] = $_;
        $count++;
    }
    @nums = sort { $a <=> $b } @nums;
}
  Nachdem wir das Input gespeichert haben, kommen wir zum Kern unseres 
menügesteuerten Skripts, einer while-Schleife, die für jede gewählte Menüoption 
jeweils die entsprechende Subroutine aufruft:
&getinput();
while ($choice !~ /q/i) {
$choice = &printmenu();
SWITCH: {
$choice =~ /^1/ && do {
&printdata(); last SWITCH; };
$choice =~ /^2/ && do {
&countsum (); last SWITCH; };
$choice =~ /^3/ && do {
&maxmin(); last SWITCH; };
$choice =~ /^4/ && do {
&meanmed(); last SWITCH; };
$choice =~ /^5/ && do {
&printhist(); last SWITCH; };
}
}
  Schauen Sie! Ein Switch! Hier »simulieren« wir mit Pattern Matching, do-Anweisungen 
und dem Label (SWITCH) eine switch-Anweisung. Bei jedem möglichen Wert von 
$choice, 1 bis 5, rufen wir eine andere Subroutine auf und verlassen mit last SWITCH 
den gesamten Block. Beachten Sie, dass ein Block mit einem Label zwar keine 
Schleife ist, Sie ihn aber mit den Schleifensteuerbefehlen genauso verlassen können.
  Welchen Wert $choice in dem SWITCH-Block überhaupt hat, bringen wir vorher mit 
dem Subroutinenaufruf &printmenu in Erfahrung. Beachten Sie, dass die while-
Schleife immer wieder das Menü ausgibt und die gewählte Operation ausführt - bis 
printmenu ein q zurückliefert. Dann bricht die Schleife ab, und das Skript wird 
beendet.
  Die Subroutine printmenu zeigt einfach die einzelnen Optionen an, liest und überprüft 
den vom Benutzer eingegebenen Wert und liefert diesen zurück:
sub printmenu {
    my $in = "";
    print "Was moechten Sie ausgeben? (Beenden mit Q): \n";
    print "1. eine Liste der Zahlen \n";
    print "2. die Anzahl und Summe der Zahlen\n";
    print "3. die kleinste und die groesste Zahl\n";
    print "4. den Durchschnitts- und den Medianwert\n";
    print "5. ein Diagramm, wie oft jede Zahl vorkommt.\n";
    while () {
        print "\nIhre Auswahl   --> ";
        chomp($in = <STDIN>);
        if ($in =~ /^\d$/ || $in =~ /^q$/i) {
            return $in;
        } else {
            print "Ungueltige Eingabe. 1-5 oder Q, bitte.\n";
 }
    }
}
  Gehen wir die im Menü gezeigten Optionen von oben nach unten durch. Möchte der 
Benutzer sich eine Liste der Zahlen anzeigen lassen, gibt er eine 1 ein, $choice erhält 
den Wert eins, und der SWITCH-Block ruft &printdata auf. Die Subroutine printdata 
durchläuft einfach das (globale) Array @nums und gibt die Elemente aus. Weil wir es 
aber übersichtlich lieben, möchten wir nach jeweils zehn Zahlen eine neue Zeile 
beginnen. Deshalb verfolgen wir in einer lokalen Variablen $i die Anzahl der 
Elemente, und wenn sie bei 10 angelangt ist, geben wir einen Zeilenvorschub aus und 
setzen sie zurück auf 1:
sub printdata {
    my $i = 1;
    print "die Zahlen: \n";
    foreach $num (@nums) {
        print "$num ";
        if ($i == 10) {
            print "\n";
            $i = 1;
        } else { $i++; }
    }
    print "\n\n";
}
  Die zweite Menüoption ist die Ausgabe von Anzahl und Gesamtsumme unserer 
Zahlen beziehungsweise der Aufruf der Subroutine &countsum:
sub countsum {
    print "Anzahl der Zahlen: ", scalar(@nums), "\n";
    print "Summe der Zahlen: ", &sumnums(),"\n\n";
}
  Diese Subroutine wiederum ruft &sumnums() auf, die die Summe aller Zahlen 
berechnet:
sub sumnums {
    my $sum = 0;
    foreach $num  (@nums) {
        $sum += $num;
    }
    return $sum;
}
In den bisherigen Versionen des Skripts haben wir die Summe gleich beim Einlesen der Zahlen berechnet, hier hingegen machen wir daraus einen separaten Vorgang. Sie könnten einwenden, dass das weniger effizient ist, zumal wir die Summe ja auch zur Berechnung des Durchschnittswerts brauchen - doch andererseits sparen wir uns dadurch Arbeit, bis sie wirklich nötig ist.
Die dritte Möglichkeit ist die Ausgabe der kleinsten und der größten Zahl. Hierfür müssen wir gar nicht viel herumrechnen - da unser Array sortiert ist, finden wir kleinstes und größtes Element am Anfang bzw. am Ende:
sub maxmin {
    print "Kleinste Zahl: $nums[0]\n";
    print "Groesste Zahl: $nums[$#nums]\n\n";
}
Entscheidet der Benutzer sich für die vierte Option, ermitteln wir Durchschnittswert und Median wie in den bisherigen Versionen des Skripts (nur dass wir uns die Summe von einer Subroutine liefern lassen):
sub meanmed {
    printf("Durchschnitt: %.2f\n", &sumnums() / scalar(@nums));
    print "Median: $nums[@nums / 2]\n\n";
}
  Bleibt nur noch die fünfte Möglichkeit, das Histogramm auszugeben. Wie in den 
bisherigen Versionen ist dies der komplexeste Teil des Skripts. Hier allerdings packen 
wir alles, was mit dem Histogramm zu tun hat, in eine Subroutine printhist, anstatt 
die nötigen Werte über das gesamte Skript zu verteilen. Das bedeutet zum einen mehr 
lokale Variablen und Rechenarbeit als für die anderen Subroutinen. Zum anderen wird 
aber der Einleseprozeß nicht von irgendwelchen Häufigkeitsberechnungen 
verlangsamt, die am Ende vielleicht gar niemand braucht - und wir haben nicht 
während des gesamten Skriptablaufs den Häufigkeits-Hash im Speicher hängen:
sub printhist {
    my %freq = ();
    my $maxfreq = 0;
    my @keys = ();
    my $space = 0;
    my $totalspace = 0;
    my $num;
    my $i;
    # freq-Hash erstellen, maxfreq festlegen
    foreach $num (@nums) {
        $freq{$num}++;
        if ($maxfreq < $freq{$num}) { 
            $maxfreq = $freq{$num} 
        }
    }
# Hash ausgeben
    @keys = sort { $a <=> $b } keys %freq;
    for ($i = $maxfreq; $i > 0; $i--) {
        foreach $num (@keys) {
            $space = (length $num);
            if ($freq{$num} >= $i) {
                print( (" " x $space) . "*");
            } else {
                print " " x (($space) + 1);
            }
            if ($i == $maxfreq) { 
                $totalspace += $space + 1; 
            }
        }
        print "\n";
    }
    print "-" x $totalspace;
    print "\n @keys\n\n";
}
Abgesehen davon, dass wir alle Häufigkeitsberechnungen in dieser Subroutine zusammengefaßt haben, hat sich an dem Vorgang selbst nicht viel geändert. Er ist nur etwas eigenständiger geworden.
Das war's schon. Listing 11.2 zeigt das gesamte Skript.
Listing 11.2: Das Skript statsmenue.pl
#!/usr/bin/perl -w
@nums = (); # Input aus Datei, eine Zahl pro Zeile
$choice = "";
# Hauptskript
&getinput();
while ($choice !~ /q/i) {
$choice = &printmenu();
SWITCH: {
$choice =~ /^1/ && do {
&printdata(); last SWITCH; };
$choice =~ /^2/ && do {
&countsum (); last SWITCH; };
$choice =~ /^3/ && do {
&maxmin(); last SWITCH; };
$choice =~ /^4/ && do {
&meanmed(); last SWITCH; };
$choice =~ /^5/ && do {
&printhist(); last SWITCH; };
}
}
# Input aus Datei lesen und dann sortieren
sub getinput {
my $count = 0;
while (<>) {
chomp;
$nums[$count] = $_;
$count++;
}
@nums = sort { $a <=> $b } @nums;
}
# das Menue. Beenden mit Q
sub printmenu {
my $in = "";
print "Was moechten Sie ausgeben? (Beenden mit Q): \n";
print "1. eine Liste der Zahlen \n";
print "2. die Anzahl und Summe der Zahlen\n";
print "3. die kleinste und die groesste Zahl\n";
print "4. den Durchschnitts- und den Medianwert\n";
print "5. ein Diagramm, wie oft jede Zahl vorkommt.\n";
while () {
print "\nIhre Auswahl --> ";
chomp($in = <STDIN>);
if ($in =~ /^\d$/ || $in =~ /^q$/i) {
return $in;
} else {
print "Ungueltige Eingabe. 1-5 oder Q, bitte.\n";
}
}
}
# die Daten ausgeben, zehn Zahlen pro Zeile
sub printdata {
my $i = 1;
print "die Zahlen: \n";
foreach $num (@nums) {
print "$num ";
if ($i == 10) {
print "\n";
$i = 1;
} else { $i++; }
}
print "\n\n";
}
# Anzahl und Summe der Elemente ausgeben
sub countsum {
print "Anzahl der Zahlen: ", scalar(@nums), "\n";
print "Summe der Zahlen: ", &sumnums(),"\n\n";
}
# Summe der Zahlen berechnen
sub sumnums {
my $sum = 0;
foreach $num (@nums) {
$sum += $num;
}
return $sum;
}
# kleinste und groesste Zahl ausgeben
sub maxmin {
print "Kleinste Zahl: $nums[0]\n";
print "Groesste Zahl: $nums[$#nums]\n\n";
}
# Durchschnitt und Median ausgeben
sub meanmed {
printf("Durchschnitt: %.2f\n", &sumnums() / scalar(@nums));
print "Median: $nums[@nums / 2]\n\n";
}
# Das Histogramm ausgeben. Haeufigkeits-Hash erstellen und ausgeben
sub printhist {
my %freq = ();
my $maxfreq = 0;
my @keys = ();
my $space = 0;
my $totalspace = 0;
my $num;
my $i;
# freq-Hash erstellen, maxfreq festlegen
foreach $num (@nums) {
$freq{$num}++;
if ($maxfreq < $freq{$num}) {
$maxfreq = $freq{$num}
}
}
# Hash ausgeben
@keys = sort { $a <=> $b } keys %freq;
for ($i = $maxfreq; $i > 0; $i--) {
foreach $num (@keys) {
$space = (length $num);
if ($freq{$num} >= $i) {
print( (" " x $space) . "*");
} else {
print " " x (($space) + 1);
}
if ($i == $maxfreq) {
$totalspace += $space + 1;
}
}
print "\n";
}
print "-" x $totalspace;
print "\n @keys\n\n";
}
Wie immer habe ich Ihnen noch nicht alles gesagt und möchte Sie in diesem Abschnitt zumindest darauf aufmerksam machen.
  In der perlsub-Manpage sind Perl-Subroutinen und wie Sie sie definieren, deklarieren 
und verwenden, detailliert beschrieben. Weil my ein Operator ist, finden Sie mehr 
Informationen über my in der perlfunc-Manpage (wir werden uns aber auch am Tag 
13 im Zusammenhang mit Gültigkeitsbereichen noch einmal mit ihm befassen). 
Sehen Sie also in diese Seiten, wenn die folgenden Themen Sie genauer 
interessieren.
Beim Aufrufen von Perl-eigenen Funktionen können Sie die Klammern um die Argumente auch weglassen, wenn Perl dann noch verstehen kann, wo die Argumente anfangen und aufhören. Das gleiche gilt auch für Ihre eigenen Subroutinen, allerdings nur unter zwei weiteren Bedingungen:
&-Präfix auf (mit dem Präfix würde Perl 
den aktuellen Wert von @_ an die Subroutine übergeben).
   
 Es ist Perl eigentlich gleichgültig, ob Sie eine Subroutine im Skript vor oder nach ihrem Aufruf deklarieren (in einigen Sprachen müssen Sie sie zuerst deklariert haben). Die Ausnahme von dieser Regel ist das Weglassen der Klammern.
Hier kommt der kleine, aber feine Unterschied zwischen Deklaration und Definition von Subroutinen ins Spiel. Jede Subroutinendefinition ist gleichzeitig auch ihre Deklaration; sagt also Perl: »Hallo, es gibt hier eine Subroutine mit diesem Namen.« Der dann folgende Block legt fest, was die Subroutine im einzelnen macht. Sie können eine Subroutine aber auch ohne diesen Block »vordeklarieren«:
sub meine_subroutine;
  Dann weiß Perl, dass mit meine_subroutine eine Subroutine gemeint ist, und 
durchsucht das Skript nach ihrer Definition.
Eine auf diese Art vordeklarierte Subroutine können Sie auch ohne Klammern um die Argumente aufrufen, wenn die eigentliche Definition erst am Ende des Skripts steht (aber irgendwo muss sie stehen, vergessen Sie sie nicht).
Es ist unter Perl-Programmierern allerdings gängige Praxis, die Klammern immer zu setzen, ob sie unbedingt nötig sind oder nicht, weil Subroutinenaufruf mit Klammern leichter lesbar und weniger fehlerträchtig - also empfehlenswert - ist.
  Wie Sie wissen, ist @_ das lokale Spezialarray einer Subroutine, das alle Argumente an 
diese Subroutine enthält. Sie können @_ aber nicht nur innerhalb, sondern auch 
außerhalb der Subroutine zum Festlegen der Argumente verwenden.
  Wenn Sie @_ zum Beispiel im Hauptkörper Ihres Skripts ein paar Elemente zuweisen 
und dann eine vordeklarierte Subroutine ohne Argumente aufrufen, übergibt Perl 
dieser Subroutine das Array @_ mit den von Ihnen gesetzten Werten:
sub meine_subroutine;
@_ = ('dies', 'das', 'sonstwas');
&meine_subroutine; # uebergibt den aktuellen Wert von @_
  Weil &meine_subroutine hier ohne Klammern, aber mit Präfix aufgerufen wird, 
übergibt Perl ihr die Elemente von @_ als Parameterliste. Das kann nützlich sein, wenn 
Sie zum Beispiel zehn verschiedene Subroutinen mit den gleichen Argumenten 
aufrufen möchten.
  Bei verschachtelten Subroutinen funktioniert es genauso. Sie können die Parameter 
einer Subroutine (nämlich den aktuellen Inhalt von @_) einfach an die nächste 
Subroutine weitergeben. Beachten Sie aber, dass die Subroutine bereits deklariert sein 
muss und dass Sie ihr »selbst gefülltes« @_ überschreiben, sobald Sie die Subroutine mit 
irgendeinem Argument aufrufen, und sei es nur ein leeres Paar Klammern.
Anonyme Subroutinen sind Subroutinen ohne Namen, die ähnlich funktionieren wie die Pointer (Zeiger) auf Funktionen in C und sozusagen erst zur Laufzeit zum Leben erwachen. Mit anonymen Subroutinen können Sie Referenzen auf eine Subroutine erzeugen und über diese Referenzen auf sie zugreifen - aber das geht hier zu weit. Wir befassen uns mit anonymen Subroutinen am Tag 19, wenn wir Referenzen behandeln.
Ich habe am Anfang des Buches erwähnt, dass kleinere Perl-Updates in erster Linie Fehler beheben, doch manchmal auch ganze neue Features mitbringen. Eins dieser Features sind Prototypen, Bestandteil von Perl seit Version 5.003. Mit Prototypen können Sie Ihre Subroutinen so deklarieren, dass sie wie Perl-eigene Funktionen aussehen und arbeiten - das heißt, dass Sie für eine Subroutine Typ und Anzahl der Argumente festlegen können, statt sie jede x-beliebige Parameterliste entgegennehmen zu lassen.
  Prototypen wirken sich nur auf Subroutinen aus, die ohne das &-Präfix aufgerufen 
werden. Sie können dieselbe Subroutine auch mit & aufrufen, nur wird der Prototyp 
dann ignoriert. Wofür Sie sich jeweils entscheiden, bleibt Ihrer Lust und Laune 
überlassen.
Eine Subroutine mit Prototyp deklarieren oder definieren Sie folgendermaßen:
sub NAME (PROTOTYP); #nur (Vor-) Deklaration
sub NAME (PROTOTYP) #Deklaration mit anschließender Definition
{ ... # Definition
}
  Wobei NAME hier für den Namen Ihrer Subroutine steht. Den PROTOTYP setzen Sie aus 
folgenden Sonderzeichen zusammen, die sich auf Anzahl und Typ der erlaubten 
Argumente repräsentieren:
$ steht für eine Skalarvariable, @ für ein Array, % für einen Hash. Ohne Backslash 
müssen @ und % am Ende stehen, weil sie Listenkontext erzwingen und alle 
restlichen Argumente »auffressen«.
   
   So verlangt zum Beispiel eine mit dem Prototyp ($$) deklarierte Subroutine zwei 
Skalarvariablen als Argumente. Der Prototyp ($$;@) fordert zwei Skalare und eine 
optionale Liste6, genauer eine Arrayvariable (die ja mit einem @ beginnt).
So sehr Prototypen Ihnen auch gefallen mögen - bedenken Sie, dass sie nur in neueren Perl-Versionen implementiert sind. Das System, auf dem Sie Ihre Skripts laufen lassen, muss also mindestens Perl 5.003 installiert haben.
Mehr Informationen zu Prototypen finden Sie in der perlsub-Manpage.
  Eine Funktion, die ich im Kapitel »übergangen« habe, ist caller. Die Funktion caller 
gibt Informationen, von wo aus eine Subroutine aufgerufen wurde (das kann bei der 
Fehlersuche behilflich sein). Für mehr Details sehen Sie in die perlfunc-Manpage.
Eine Subroutine ist eine Möglichkeit, häufig gebrauchten Code zusammenzufassen oder ein langes Skript in kleinere »Portionen« aufzuteilen. Heute haben Sie gelernt, wie Sie Subroutinen deklarieren, definieren, aufrufen und Werte in sie hinein und wieder heraus bekommen. In diesem Zusammenhang haben wir auch über den Gültigkeitsbereich von globalen und lokalen Variablen gesprochen.
  Subroutinen definieren Sie mit dem Schlüsselwort sub, dem Namen der Subroutine 
und einem Block, der die auszuführenden Anweisungen enthält. Innerhalb dieses 
Blocks können Sie mit my lokale Variablen deklarieren, die nur für diese Subroutine 
sichtbar und verfügbar sind. An die übergebenen Argumente kommen Sie heran, 
indem Sie auf das lokale Spezialarray @_ zugreifen, und mit der Funktion return 
geben Sie einen Wert oder eine Wertliste aus der Subroutine zurück. Wenn es relevant 
ist, in welchem Kontext die Subroutine aufgerufen wurde, ermitteln Sie ihn mit der 
Funktion wantarray.
  Sie rufen Subroutinen mit einem optionalen &-Präfix, dem Namen der Subroutine und 
den Argumenten in Klammern auf. Perl gibt alle Argumente in einer linearen, flachen 
Liste an das Array @_. Hashes und verschachtelte Listen werden dabei expandiert.
Frage:
 Was ist der Unterschied zwischen Subroutinen und Funktionen?
Antwort:
 Im Grunde gibt es keinen Unterschied, beide bezeichnen vom Konzept her 
dasselbe. Deswegen kann man die beiden Begriffe zur Unterscheidung 
zwischen bestimmten Arten von Funktionen/Subroutinen verwenden. Ich zum 
Beispiel unterscheide in diesem Buch zwischen den in Perl eingebauten und 
Ihren selbstdefinierten Funktionen.
Der Unterschied zwischen selbstdefinierten und Perl-eigenen Funktionen ist aber nicht nur ein begrifflicher. Anders als Perl-Funktionen erwarten Subroutinen (zumindest ohne Prototypen) keine bestimmte Anzahl und Art von Argumenten. Auch die Regeln, wann Sie die Klammern um die Argumente weglassen können, sind unterschiedlich. Doch in zukünftigen Perl-Versionen wird das Verhalten von Subroutinen dem der Perl-eigenen Funktionen mehr und mehr ähneln - die Perl-Autoren arbeiten daran.
Frage:
 Subroutinenaufrufe mit & sehen irgendwie seltsam aus. Kann ich das & weglassen?
Antwort:
 Ja, das &-Präfix ist optional. Ob Sie es verwenden oder nicht, liegt ganz allein 
bei Ihnen.
Frage:
 Ich möchte zwei Arrays an eine Subroutine übergeben und auch zwei Arrays 
zurückbekommen. Aber die beiden Arrays werden auf dem Hin- und Rückweg in 
einer einzelnen Liste zusammengeschmissen. Wie halte ich sie auseinander?
Antwort:
 Am besten machen Sie das mit Referenzen, und die besprechen wir am Tag 
19. Eine andere Möglichkeit wäre, innerhalb der Subroutine einfach zwei 
globale Arrays zu verändern, anstatt ihr die Daten in einer Liste zu 
übergeben.
Frage:
 Aber globale Variablen sind böse, schlecht und verwerflich.
Antwort:
 Na, das kommt darauf an, mit wem Sie sprechen. Manche Probleme lassen 
sich in Perl durchaus am besten mit globalen Variablen lösen. Wenn Sie 
vorsichtig mit ihnen umgehen, sie »sauber« deklarieren und die Grenzen ihrer 
Gültigkeitsbereiche klar ziehen, dann kommen Sie um die Nachteile von 
globalen Variablen auch herum. Wir befassen uns übermorgen genauer mit 
Gültigkeitsbereichen.
Frage:
 Ich verstehe das nicht, sind Perl-Subroutinen nun call-by-value oder call-by-
reference?
Antwort:
 Weil Perl keine formalen Parameter hat, ist das gar nicht so leicht zu 
beantworten. Prinzipiell ist die Übergabe der Parameterliste an die 
Subroutine eine Referenzübergabe (call-by-reference): Wenn Sie direkt auf @_ 
zugreifen und zum Beispiel eine Skalarvariable verändern, ändert sich ihr 
Wert auch außerhalb der Subroutine. Beachten Sie aber, dass Hashes und 
verschachtelte Arrays expandiert werden - bei Arrays haben Sie dann 
bestenfalls eine Referenzübergabe der Einzelteile, bei Hashes eine reine 
Wertübergabe (call-by-value). Am einfachsten ist, alle Elemente von @_ 
lokalen Variablen zuzuweisen und mit diesen weiterzuarbeiten. Das 
entspräche einer Call-by-value-Übergabe aller Argumente - Ihren 
Originalvariablen könnte nichts mehr passieren.
Der Workshop enthält Quizfragen, die Ihnen helfen sollen, Ihr Wissen zu festigen, und Übungen, die Sie anregen sollen, das eben Gelernte umzusetzen und eigene Erfahrungen zu sammeln. Versuchen Sie, das Quiz und die Übungen zu beantworten und zu verstehen, bevor Sie zur Lektion des nächsten Tages übergehen.
return explizit 
festlegen?
 return aus einer Subroutine zurückgibt, ein Skalar oder eine 
Liste?
 my deklarierte Variablen verfügbar?
 my eine lokale Variable deklarieren und es bereits eine 
gleichnamige globale Variable gibt?
 @_? Wo haben Sie Zugriff auf @_?
 wantarray? Wozu könnten Sie es gebrauchen?
zum Beispiel - Beispiel zum).
 @gemeinsam = &schnittmenge(@liste1, @liste2);
sub schnittmenge {
my (@eins, @zwei) = @_;
my @final = ();
OUTER: foreach my $el1 (@eins) {
foreach my $el2 (@zwei) {
if ($el1 eq $el2) {
@final = (@final, $el1);
next OUTER;
}
}
}
return @final;
}
$wie_oft= &suche(@input, $suchwort);
sub suche {
my (@in, $suchwort) = @_;
my $count = 0;
foreach my $str (@in) {
while ($str =~ /$suchwort/og) {
$count++;
}
}
return $count;
}
Hier die Antworten auf die Workshop-Fragen aus dem vorigen Abschnitt.
&subroutine(); # beides: &-Präfix und Klammern
&subroutine(1,2) # mit Argumenten
subroutine(1,2) # & ist optional
    sub NAME {
   # Körper der Subroutine
}
 return gibt die Subroutine das Ergebnis der letzten 
Auswertung im Block zurück.
 return einen Skalar oder eine Liste zurückgibt, bestimmen Sie.
 My-Variablen sind nur in dem Block, in dem sie deklariert wurden, sichtbar und 
verfügbar, nirgendwo sonst - nicht einmal in von dort aufgerufenen Subroutinen.
 my deklarierte Variablen verstecken gleichnamige globale Variablen. Erst wenn 
Perl den Gültigkeitsbereich der my-Variablen verläßt, sind ihre globalen 
»Namensvettern« wieder verfügbar.
 @_ ist die Parameterliste der aktuellen Subroutine - das lokale Spezialarray, das 
alle Argumente an diese Subroutine enthält. Normalerweise greift man innerhalb 
der Subroutine darauf zu, aber man kann @_ auch außerhalb verwenden (siehe 
Abschnitt »Prototypen«).
 @_ lokalen 
Variablen zuweisen (die Sie ja benennen können, wie Sie wollen). Allerdings 
entspricht das einer Call-by-value-Wertübergabe, Sie arbeiten also nur mit den 
Werten der Argumente weiter, nicht mit den Originalargumenten außerhalb der 
Subroutine.
 wantarray liefert wahr zurück, wenn die Subroutine in Listenkontext 
aufgerufen wurde. Mit ihr könnten Sie herausfinden, in welchem Kontext die 
Subroutine aufgerufen wurde, und je nachdem (ob Listen- oder Skalarkontext) ein 
entsprechendes Ergebnis zurückgeben.
    sub print_argumente {
    my $zeile = 1;
    foreach my $arg (@_) {
        print "$zeile. $arg\n";
        $zeile++;
    }
}
     sub string_umkehren {
    my @str = split(/\s+/, $_[0]);
    return join(" ", reverse @str);
}
     sub quadrate {
    my @ergebnis = ();
    foreach my $el (@_) {
        if ($el !~ /\D+/) {
            @ergebnis = (@ergebnis, $el**2);
        }
    }
    return @ergebnis;
}
 liste1 und 
liste2 innerhalb der Subroutine noch zwei Listen (@eins und @zwei) seien. Sind 
sie aber nicht - all ihre Elemente werden fein säuberlich in einer Liste 
aneinandergereiht, an @_ weitergegeben und @eins zugewiesen. Für dieses 
Beispiel wären (momentan) zwei globale Variablen besser geeignet.
 @input und dann das $suchwort enthält, weisen wir in der Subroutine auch 
das Suchwort der lokalen Variablen @input zu. Sie erinnern sich, wie gefräßig 
Listenvariablen auf der linken Seite einer Zuweisung sind - sie schlucken alle noch 
vorhandenen Elemente, und für das lokale $suchwort bleibt nichts mehr übrig. 
Tauschen Sie die Argumente einfach um, und weisen Sie zuerst das Suchwort und 
dann die Liste zu.
     sub rueckwaerts {
    my $in = "";
    if (wantarray()) {  # Listenkontext
        my @inliste = ();
        while () {
            print 'Ihre Eingaben bitte: ';
            $in = <STDIN>;
            if ($in ne "\n") {
                @inliste = ($in, @inliste); # Reihenfolge 
                                            # umkehren
            }
            else { last; }
        }
        return @inliste;
    }
    else {            # Skalarkontext
        print 'Ihre Eingabe bitte: ';
        chomp($in = <STDIN>);
        return reverse $in;
    }
}
 #!/usr/bin/perl -w
$/ = ""; # Absatzeingabe-Modus
while (<>) {
while (/<IMG\s([^>]+)>/ig) {
&bild_atts_ermitteln($1);
}
}
sub bild_atts_ermitteln {
my $roh = $_[0];
my %atts = ();
while ($roh =~ /([^\s=]+)\s*=\s*("([^"]+)"|[^\s]+\s*)/ig) {
if (defined $3) {
$atts{ uc($1) } = $3;
} else { $atts{ uc($1)} = $2; }
}
if ($roh =~ /ISMAP/i) {
$atts{'ISMAP'}= "Yes";
}
&print_atts(%atts);
}
sub print_atts {
my %atts = @_;
print '-' x 15;
print "\nImage: $atts{'SRC'}\n";
foreach my $key ("WIDTH", "HEIGHT",
"BORDER", "VSPACE", "HSPACE",
"ALIGN", "ALT", "LOWSRC", "ISMAP") {
if (exists($atts{$key})) {
$atts{$key} =~ s/[\s]*\n/ /g;
print " $key: $atts{$key}\n";
}
}
}





Von Funktionen wird erwartet, dass sie einen sinnvollen Wert zurückgeben, von Subroutinen aber nicht. Doch irgend etwas geben Subroutinen in Perl immer zurück - und über den Sinn läßt sich streiten.
Selbst wenn eine Subroutine sich selber aufruft, erhält sie mit jedem Aufruf eine neue Kopie der
my-Variablen.