Eigene Funktionen schreiben
Wenn Sie diesem Tutorial bis hierhin gefolgt sind, sind Ihnen immer
wieder "Dinge" begegnet, die als "Funktionen" bezeichnet wurden, zum
Beispiel say, push und andere.
Funktionen sind immer dann gut, wenn man ein Stück Quellcode an verschiedenen Stellen im Programm braucht.
Anstatt dieses Stück Quellcode überall dorthin zu kopieren, wo man es braucht, schreibt man es an einer Stelle in eine sogenannte Funktion, und ruft diese Funktion dann auf, wenn man den Code braucht.
Hier zum Beispiel soll an mehreren Stellen im Programm eine positive Zahl von der Tastatur gelesen werden, und das so lange versucht werden, bis der Benutzer endlich nachgegeben hat:
my $a; my $b; $a = $*IN.get; while $a <= 0 { $a = $*IN.get; } $b = $*IN.get; while $b <= 0 { $b = $*IN.get; } say "Zwei positive Zahlen: $a und $b";
Hier sind vier Zeilen in dem Programm, die sich wiederholen, und nur durch den Namen einer Variable unterscheiden.
Und so sieht es aus, wenn man das ganze in eine Funktion packt:
sub positive_zahl { my $zahl = $*IN.get; while $zahl <= 0 { $zahl = $*IN.get; } return $zahl; } my $a = positive_zahl; my $b = positive_zahl; say "Zwei positive Zahlen: $a und $b";
Dieses Beispiel macht genau das gleiche wie das vorherige, nur dass es intern anders aufgebaut ist.
Am Anfang wird in der Zeile
sub positive_zahl {
eine Funktion oder Subroutine, auf Englisch kurz
sub definiert, mit dem Namen positive_zahl.
Für Funktionsnamen gelten die gleichen Regeln wie für Variablennamen, nur dass sie offensichtlich nicht mit einem Sonderzeichen anfangen: Sie müssen mit einem Buchstaben oder Unterstrich anfangen, und können aus Buchstaben, Unterstrichen und Ziffern bestehen.
Nach dem Funktionsnamen kommt einen öffnende geschweifte Klammer
{, und danach kommt der eigentliche Inhalt der Funktion,
der so lange geht, bis die geschweifte Klammer von }
geschlossen wird.
In der Funktion taucht die Anweisung return auf,
hinter der ein Ausdruck steht - hier die Variable
$zahl.
Das sorgt dafür, dass die Funktion beendet wird, sobald die Stelle
erreicht wird, an der return steht, und der Ausdruck
dahinter wird an den Aufrufer zurückgegeben.
Wenn z.B. ein return 4; in der Funktion steht, und die
Funktion mit $a = positive_zahl; aufgerufen wird, hat
danach die Variable $a den Wert 4.
Gültigkeitsbereiche für Variablen
Versuchen Sie, folgendes Programm auszuführen:
sub meine_berechnung ($x){ my $y = 2 * $x; return $y * $y; } say $y; # FEHLER!
Die letzte Zeile wird einen Fehler ausgeben, der sagt, dass die
Variable $y nicht deklariert sei.
Tatsächlich wurde sie zwar definiert, aber innerhalb einer Funktion, und in diesem Beispiel wird versucht, auf sie außerhalb der Funktion zuzugreifen. Dort ist sie aber nicht bekannt.
Man sagt, dass die Funktion einen eigenen Gültigkeitsbereich für Variablen erzeugt, auf Englisch "Scope" genannt.
In der Funktion kann man auf Variablen im äußeren Scope zugreifen, wenn sie vorher deklariert wurden:
my $x; sub say_x { say $x; } sub change_x { $x = 0; } $x = 3; say_x; # Ausgabe: 3 change_x; # $x wird jetzt auf 0 gesetzt say_x; # Ausgabe: 0
Wenn man innerhalb einer Funktion eine Variable mit my
deklariert, die außerhalb dieser Funktion schon vorhanden ist, gilt
innerhalb dieser Funktion nur die neu deklarierte "lexikalische"
Variable, außerhalb der Funktion aber wieder die alte:
my $x = 3; say $x; # Ausgabe: 3 sub irgendeine_funktion { my $x; $x = 4; say $x; # Ausgabe: 4 } irgendeine_funktion(); say $x; # Ausgabe: 3
Diese Regeln gelten nicht nur für Funktionen, sondern für alle Blöcke, d.h. für (fast) alles, was in geschweiften Klammern steht.
In den meisten Programmiersprachen gelten übrigens andere Regeln für den Gültigkeitsbereich von Variablen.
Motivation
Am Anfang sehen diese Regeln kompliziert und vielleicht auch willkürlich aus, aber es gibt sehr gute Gründe dafür.
Der wichtigste ist, dass es sonst sehr schnell zu unangenehmen Namenskollisionen kommen kann:
sub fakultaet($x){ my $produkt = 1; for 1 .. $i -> $i { $produkt = $produkt * $x; } return $produkt; } # irgendwo ganz anders im Code: # Berechne die 10. Zeile des Pascalschen Dreiecks: # http://de.wikipedia.org/wiki/Pascalsches_Dreieck loop(my $i = 0; $i < 10; $i++){ say fakultaet(10) / (fakultaet($i) * fakultaet(10 - $i)); }
Wenn es kein Scoping, also keine verschiedenen Gültigkeitsbereiche für Variablen gäbe, würde folgendes passieren:
- In der Schleife wird
$iauf 0 gesetzt. fakultaet(10)wird aufgerufen, darin wird am Ende derfor-Schleife$iauf 10 gesetzt.- Ab jetzt enthält
$iden Wert 10, d.h. der Aufruffakultaet($i)wird nachfakultaet(10)überesetzt. Nach diesem Aufruf steht$iimmer noch auf 10. - Der dritte Aufruf
fakultaet(10-$i)wird zufakultät(10-10), alsofakultaet(0). In deren Berechnung wird die Liste1..0durchlaufen, diese Liste ist aber leer, d.h. die Variable$iändert ihren Wert nicht. - Nach dieser Zeile hat
$iden Wert 10, die Bedingung$i < 10ist also nicht mehr erfüllt, die Schleife bricht ab.
Ohne Scoping würde die Schleife also nicht wie erwartet die 10 Werte (1, 10, 45, 120, 210, 252, 210, 120, 45, 10,) ausgeben, sondern nur einen einzigen Wert, 1.
Das Problem war, dass in zwei verschiedenen Bereichen des Codes Variablen mit gleichen Namen verwendet wurden.
Programme können leicht mehrere Zehntausend Zeilen lang werden - der Linux Kernel hat sogar 6 Millionen Zeilen Programmcode.
In derartig großen Projekten ist es de facto unmöglich, Namenskollision zu vermeiden, wenn die Programmiersprache keine effektive Möglichkeit gibt, damit umzugehen.