Perl-Programmierung: Subroutinen
Vorbemerkung
[Bearbeiten]Funktionen bzw. Prozeduren werden in Perl Subroutinen genannt. Subroutinen werden immer dann verwendet, wenn sich ein Vorgang mehrfach verwenden lässt. Teilstücke des Programms werden aus dem Hauptprogramm ausgelagert und durch den Aufruf einer Subroutine eingebunden. Dadurch muss der Vorgang nur einmal programmiert werden. Auch Änderungen des Programms werden erleichtert, weil diese ebenfalls nur an einer Stelle vorgenommen werden müssen. Ein ausgiebiger Gebrauch von Subroutinen ist also empfehlenswert.
Für Experten: Bei einigen älteren Programmiersprachen wird zwischen Prozeduren und Funktionen unterschieden. Funktionen erzeugen Rückgabewerte, Prozeduren nicht. Diese Unterscheidung ist in Perl unerheblich.
Allgemeine Deklaration
[Bearbeiten]Aufbau
[Bearbeiten] sub SUBROUTINENNAME{
DEKLARATIONSBLOCK
ANWEISUNGSBLOCK
return RÜCKGABEWERT;
}
Die Deklaration von internen Variablen einer Subroutine erfolgt entweder mit local oder mit my.
Beispiel:
my $param1 = 0;
local $param2 = 0;
Bei der Verwendung von local steht die Variable auch untergeordneten Subroutinen zur Verfügung.
Die Parameterübergabe erfolgt in Perl nicht wie üblich im Kopfteil der Subroutine. Stattdessen steht das vordefinierte Array @_ zur Verfügung.
Beispiel:
my $param1 = $_[0];
local $param2 = $_[1];
...
Prototyp
[Bearbeiten]Ein Prototyp hat die Aufgabe, dem Hauptprogramm den Namen und die Parametertypen mitzuteilen. Er schreibt sich wie ein reduzierter Kopfteil einer Subroutine.
Beispiel, Subroutine mit zwei einfachen Übergabeparametern und einem optionalen einfachen Übergabeparameter:
sub SUBROUTINENNAME($$;$)
Beispiel, Subroutine mit einer Übergabeliste:
sub SUBROUTINENNAME(@)
Aufruf
[Bearbeiten]Subroutinen werden mit dem Symbol & gefolgt vom Namen der Subroutine aufgerufen.
Möglichkeit 1: &SUBROUTINENNAME(PARAMETERLISTE);
Möglichkeit 2: SUBROUTINENNAME(PARAMETERLISTE);
Möglichkeit 3: &SUBROUTINENNAME PARAMETERLISTE;
Bei Verwendung eines Prototyps oder einer Paket-Subroutine gibt es auch noch die
Möglichkeit 4: SUBROUTINENNAME PARAMETERLISTE;
Subroutinen lassen sich ohne und mit Parameterlisten aufrufen. Die Parameter finden sich innerhalb der Subroutine in der Struktur @_ wieder und können mit $_[] ($_[0], $_[1] ... usw.) separat angesprochen werden.
Beispiel:
sub groesser{
if ($_[0] > $_[1]){
$_[0]; #ergebnis
}else{
$_[1]; #ergebnis = rueckgabewert
}
}
Aufruf mit &groesser(24,15) liefert als Ergebnis 24. ($_[0]=24 der Rückgabewert).
Mit lokalen Variablen:
sub groesser{
local ($a,$b) = ($_[0],$_[1]);
if ($a > $b){
$a;
}else{
$b;
}
}
Dieses Beispiel liefert das gleiche Ergebnis wie das vorhergehende.
Das in C, C++ und anderen Programmiersprachen verwendete return zur Rückgabe eines einzelnen Wertes ist in Perl am Ende einer Subroutine nicht zwingend nötig. Von der Subroutine wird immer der Rückgabewert des letzten Befehls zurück gegeben. Die folgende Subroutine test liefert zum Beispiel den Wert 1, da dieser bei erfolgreicher Ausführung der festgelegte Rückgabewert von print ist.
sub test{
print "Nichts zu tun.\n\n";
}
Kontext
[Bearbeiten]Eine Besonderheit in Perl ist, dass der Rückgabewert einer Subroutine nicht nur von den Parametern abhängt, sondern auch vom Kontext, in dem der Aufruf erfolgt. Deshalb sind solche Unterprogramme keine Funktionen im strengen Sinne. Das Schlüsselwort sub in Perl ist von "subroutine" ("Unterprogramm") abgeleitet.
Den Kontext muss auch bei der Verwendung einiger eingebauter Subroutinen beachten werden, zum Beispiel bei der Verwendung der Subroutine each.
my %hash = (Kuh => 'Gras', Kamel => 'Wasser', Gnu => 'Freie Software');
while( my $tier,$topic = each(%hash) ){
print $tier," => ",$topic,"\n"
}
while( my ($tier,$topic) = each(%hash) ){
print $tier," => ",$topic,"\n"
}
Liefert die Ausgabe:
=> Gnu => Kamel => Kuh Gnu => Freie Software Kamel => Wasser Kuh => Gras
Bei der zweiten Schleife liefert each durch die Klammer ein Schlüssel-Wert Paar. In der Schleife darüber wird der Variable $topic bei jedem Schleifendurchlauf ein Schlüsselwert zugewiesen.
Um dies in eigenen Subroutinen nutzen zu können, liefert die Perl-Subroutine wantarray einen wahren Wert, wenn als Rückgabe der aufrufenden Subroutine eine Liste erwartet wird.
Externe Subroutinen
[Bearbeiten]Viele Subroutinen sind bereits in den vielen hundert Modulen im CPAN und anderswo als freie Software verfügbar. Um dabei Namenskonflikte zu vermeiden, werden zusammengehörige Subroutinen in einem Namensraum zusammengefasst.
Ein Beispiel ist das Modul File::Temp. Es verwendet den Namensraum File::Temp und enthält zwei Subroutinen, eine zur Erstellung von temporären Dateien (tempfile) und eine für Verzeichnisse (tempdir).
Namensräume sind in Perl hierarchisch aufgebaut und sollten vom Programmierer sinnvoll gewählt werden. Außerdem ist, wie man sieht, der Namensraum meist identisch mit dem Modul. Umfangreiche Module können aber auch viele Namensräume nutzen.
Der gewählte Name sollte in einem Zusammenhang mit der Funktion des Moduls stehen. So stehen alle Module im Namensraum File in einem Zusammenhang mit Dateien.
Laden eines Moduls mit use
[Bearbeiten]use File::Temp;
In dieser Form wird das Modul während der Übersetzungszeit geladen. Außerdem wird eine spezielle Subroutine in dem zu ladenden Modul aufgerufen. Ihr Name ist import.
use File::Temp ();
Schreibt man hinter das Modul eine leere Liste, wird der Aufruf von import übergangen.
use File::Temp qw/ tempfile tempdir /;
Für Experten: In dieser Form zeigt sich woher die Bezeichnung use stammt. Subroutinen können häufig in den aktuellen Namensraum geladen werden, sie werden quasi importiert. Genau das soll mit dieser Form von use ausgedrückt werden.
Erstellung
[Bearbeiten]Bibliothek
[Bearbeiten]Eine Bibliothek ist eine Datei, die Subroutinen enthält. Der Dateiname endet mit *.pm, und die letzte Zeile ist:
1;
Modul
[Bearbeiten]Einbindung und Aufruf
[Bearbeiten]Eine Bibliothek wird mit require eingebunden:
require("DATEINAME.pl");
Subroutinen aus Bibliotheken werden wie interne Subroutinen aufgerufen.
Ein Modul wird mit use eingebunden
use MODULNAME;
und mit
MODULNAME::SUBROUTINENNAME();
aufgerufen.