# Subroutinen

Achtung: Diese Funktion führt bei Topologien hoher Komplexität zu suboptimalen Resultaten!
– Meldung der Autocad-Zusatzapplikation C.A.T.S.

# Was sind Subroutinen?

Subroutinen entsprechen den Funktionen bzw. Methoden in JavaScript. Der Vorteil solcher Subroutinen liegt klar auf der Hand. Redundanter Quellcode, also Programmcode, der sich an mehreren Stelle wiederholt, kann somit in ein eigenes kleines Unterprogramm ausgelagert und an beliebiger Stelle des Hauptprogramms aufgerufen werden. Dies erzeugt vor allem Übersicht im Quelltext und ermöglicht gleichzeitig die Wiederverwendung einmal gelöster Aufgaben. Denn eine einmal funktionierende Subroutine kann bequem in ein anderes Perl-Script übertragen werden.

Obwohl die Subroutinen in Perl diesen speziellen Namen besitzen, unterscheiden sie sich doch kaum von den Methoden oder Funktionen aus JavaScript. So ist es durchaus möglich, an eine Subroutine Parameter zu übergeben oder einen Rückgabewert zu erhalten. Lediglich die Vorgehensweise unterscheidet sich von den Funktionen in JavaScript.

# Subroutinen erstellen

Während Sie in JavaScript das Schlüsselwort function verwenden, um eine Funktion zu definieren, wird in Perl das Schlüsselwort sub (als Abkürzung für Subroutine) verwendet. Nach dem Schlüsselwort sub folgt dann der Bezeichner der Routine. Die Namensregeln, die auch schon für die Variablen gelten, sind auch für das Bezeichnen von Subroutinen gültig. Sie dürfen also keine deutschen Umlaute oder ß verwenden, und als einziges Sonderzeichen ist nur der Unterstrich erlaubt. Vor den Bezeichner einer Subroutine wird übrigens kein spezielles Zeichen gesetzt, wie es etwa bei Skalaren, Listen oder Hashes der Fall ist. In geschweiften Klammern folgt anschließend der Anweisungsblock.

sub [Bezeichner]  
{  
  [Anweisungsblock]  
  ...  
}

Denken Sie daran, dass der Anweisungsblock einer Subroutine nur dann ausgeführt wird, wenn die Subroutine an einer beliebigen Stelle im Programmcode auch aufgerufen wird.

Innerhalb des Anweisungsblocks einer Subroutine dürfen Sie beliebige Anweisungen notieren. Auch der Aufruf einer anderen Subroutine ist möglich.

Beim Erstellen einer Subroutine haben Sie eigentlich alle Freiheiten, die Sie sich wünschen können. Immerhin ist die Position, an der die Subroutine stehen soll, nicht vorgegeben. Sie können eine selbst definierte Routine also am Anfang des Perl-Scripts, am Ende des Scripts oder mitten im Script notieren bzw. definieren. Die letzte Variante, mitten im Quelltext, ist jedoch ungünstig, da dies wieder zu geringer Lesbarkeit des Quellcodes führt. Außerdem könnte so der Eindruck entstehen, dass gleichzeitig an dieser Stelle auch die Routine ausgeführt wird, ohne aufgerufen werden zu müssen. Am sinnvollsten ist es, alle Routinen am Script-Anfang zu notieren (natürlich erst nach der Hashbang) und mit Kommentaren zu erläutern, was diese Routine bewirkt. Selbst nach einem halben Jahr werden Sie Ihren eigenen Quellcode dann noch immer ohne Probleme lesen und verstehen können.

# Subroutinen aufrufen

Eine selbst definierte Subroutine aufzurufen ist recht simpel. Sie müssen lediglich den Routinen-Bezeichner notieren, gefolgt von runden Klammern, und schließen dann die Anweisung mit einem Semikolon ab. Die folgende Routine soll bei ihrem Aufruf den Text »Guten Tag!« ausgeben.

sub guten_tag  
{  
  print "Guten Tag!";  
}

Der Aufruf dieser Routine könnte dann folgendermaßen aussehen:

guten_tag();

Ich werde diesen Vorgang an einem weiteren Beispiel konkretisieren.

#!/usr/bin/perl -w
use CGI qw(:standard);

my $ergebnis = 0;
my $a = 15;
my $b = 30;

# Subroutine zum Ausgeben des Ergebnises der Rechnung
sub ergebnisAusgeben
{
  print "Das Ergebnis aus $a + $b ist <b>$ergebnis</b><br>";
}

print header();

$ergebnis = $a + $b;
ergebnisAusgeben();
$a = 50;
$b = 20;
$ergebnis = $a + $b;
ergebnisAusgeben();

Listing 3.1: Beispiel-Script, in dem eine Subroutine definiert und aufgerufen wird

In Listing 3.1 werden zuerst drei Skalare definiert: $ergebnis, $a und $b. Anschließend folgt die Definition der Subroutine ergebnisAusgeben. Die einzige Aufgabe dieser Routine ist es, die Werte der drei Skalare in HTML formatiert auszugeben. Nach der Definition der Routine wird der MIME-Typ für HTML-Dokumente an den Browser gesendet und dem Skalar $ergebnis die Summe aus $a und $b zugewiesen. Dann folgt der Aufruf der selbst definierten Routine mit der Anweisung ergebnisAusgeben(). Den Skalaren $a und $b wird jeweils ein neuer Wert zugewiesen und die Summe erneut im Skalar $ergebnis gespeichert. Nun folgt ein weiterer Aufruf der Routine ergebnisAusgeben.

Wird dieses Perl-Script mit einem Browser geöffnet, erhalten Sie die folgende Ausgabe:

Das Ergebnis aus 15 + 30 ist 45
Das Ergebnis aus 50 + 20 ist 70

Zwar ist die Aufgabe dieser Routine nicht gerade weltbewegend, trotzdem erledigt sie diese effektiv.

# Local und my

Zur weiteren Verwendung von Subroutinen in Verbindung mit Variablen sind noch ein paar Grundlagen zu erläutern, immerhin gibt es die Möglichkeit, innerhalb von Subroutinen Variablen zu definieren. Wenn Sie z. B. eine Subroutine schreiben, die komplexe Rechenaufgaben bewältigen soll, werden Sie dies in den seltensten Fällen ohne Hilfsvariablen lösen können, da Sie mit Sicherheit den einen oder anderen Wert zwischenspeichern müssen, um ihn weiterverarbeiten zu können.

Die Definition einer Variablen innerhalb einer Routine erfolgt auf dem gleichen Wege wie sonst auch: mit der Anweisung my. Wenn Sie Subroutinen schreiben, die Sie auch in anderen Programmen verwenden möchten, kann es vorkommen, dass Sie innerhalb einer Routine eine Variable definieren, die außerhalb der Routine, also im Hauptprogramm, bereits existiert. In solchen Fällen kann es durchaus zu Fehlern kommen, da beide Variablen miteinander »kollidieren«.

Mit der Anweisung my weisen Sie einer Variablen aber gleichzeitig auch einen Gültigkeitsbereich zu, d. h. einen Bereich, innerhalb dessen Operationen mit der Variablen ausgeführt werden dürfen. Wenn Sie innerhalb einer Subroutine eine Variable mit my definieren, kann nur in dieser Subroutine auf die Variable zugegriffen werden, nicht aber vom Hauptprogramm aus. Auch wenn Sie eine weitere Subroutine aus der aktuellen Subroutine aufrufen, steht die mit my definierte Variable nicht zur Verfügung.

Etwas anders sieht es mit Variablen aus, die mit my im Hauptprogramm definiert wurden. Diese Variablen stehen im gesamten Programm und in allen Subroutinen zur Verfügung.

Schematische Darstellung der Gültigkeitsbereiche Abbildung 3.1: Schematische Darstellung der Gültigkeitsbereiche

Der Gültigkeitsbereich einer Variablen bezieht sich immer auf einen Anweisungsblock. Jede Subroutine stellt einen einzelnen Anweisungsblock dar. Nur innerhalb dieses Anweisungsblocks steht eine mit my definierte Variable zur Verfügung. Das Hauptprogramm inklusive aller Subroutinen interpretiert Perl jedoch als einen einzelnen Anweisungsblock. Aus diesem Grund können Sie sowohl in routine1 als auch in routine2 auf den Skalar $var_h zugreifen. Der Zugriff auf den Skalar $var_r1 ist nur in routine1 möglich und der Zugriff auf den Skalar $var_r2 nur in der routine2.

Im Gegenzug können Sie aber mit der Anweisung local Variablen so definieren, dass sie im aktuellen Anweisungsblock, aber auch in untergeordneten Anweisungsblöcken zur Verfügung stehen. Wenn Sie also mit local eine Variable innerhalb einer Subroutine definieren, steht diese Variable auch all den Subroutinen zur Verfügung, die aus der Subroutine aufgerufen werden.

Schematische Darstellung der Gültigkeitsbereiche Abbildung 3.2: Schematische Darstellung der Gültigkeitsbereiche

Im Hauptprogramm können Sie lediglich auf den Skalar $var_h zugreifen, da durch die Variablendefinitionen mit local in routine1 und routine2 der Gültigkeitsbereich der Variablen auf untergeordnete Anweisungsblöcke beschränkt ist. Aus diesem Grund ist in routine2 der Zugriff sowohl auf $var_h und $var_r2 als auch auf $var_r1 möglich, da routine2 ein untergeordneter Anweisungsblock von routine1 ist.

# Doppeldefinition

Durch die Einschränkung der Gültigkeitsbereiche auf bestimmte Anweisungsblöcke mit my und local sind auch Doppeldefinitionen von Variablen möglich. Dies verdeutlicht das Listing 3.2.

#!/usr/bin/perl -w
use CGI qw(:standard);

print header();

my $my = 1;
local $loc = 2;
$glob = 3;

print "main:: $my - $loc - $glob<br>"; 

sub sub1 {
  my $my = 11;
  local $loc = 22;
  $glob = 33;
  print "sub1:: $my - $loc - $glob<br>";
  sub2();
  print "sub1:: $my - $loc - $glob<br>";
}

sub sub2 {
  print "sub2:: $my - $loc - $glob<br>";
  $my = 111;
  $loc = 222;
  $glob = 333;
  print "sub2:: $my - $loc - $glob<br>";
}

sub1();

print "main:: $my - $loc - $glob<br>";

Listing 3.2: Perl-Script, das die Gültigkeitsbereiche von Variablen veranschaulichen soll

Die Ausgabe, die das Listing 3.2 erzeugen sollte, sieht folgendermaßen aus:

main:: 1 – 2 – 3
sub1:: 11 – 22 – 33
sub2:: 1 – 22 – 33
sub2:: 111 – 222 – 333
sub1:: 11 – 222 – 333
main:: 111 – 2 – 333

Sowohl im Hauptprogramm als auch in der Subroutine sub1 werden die Variablen $my und $loc definiert. Dies führt dazu, dass die Werte, die den Skalaren $my und $loc im Hauptprogramm zugewiesen wurden, auf einen Ablagestapel gelegt und in sub1 mit neuen Werten belegt werden. In sub2 stehen zwei verschiedene Werte zur Verfügung. Der Wert der Variablen $my lautet 1. Dies ist nicht weiter verwunderlich, denn sobald die sub1 verlassen wird, bekommt $my wieder den Wert zurück, der ihr im Hauptprogramm zugewiesen wurde, da der in sub1 zugewiesene Wert nur dort gültig ist. Der Wert von $loc lautet 22. Auch dies ist korrekt, da der Wert von $loc, der in sub1 zugewiesen wurde, auch in untergeordneten Anweisungsblöcken zur Verfügung steht. Nachdem die Ausgabe der Variablen erfolgte, werden den Skalaren neue Werte zugewiesen. Der Skalar $my erhält den Wert 111 und der Skalar $loc den Wert 222. Wenn die Subroutine sub2 verlassen wurde, besitzt $loc in sub1 zwar noch den Wert 222, $my besitzt nun aber wieder den Wert 11. Zurück im Hauptprogramm lautet der Wert von $loc wieder 2, da der in sub1 zugewiesene Wert nicht für übergeordnete Anweisungsblöcke verfügbar ist. Der Skalar $my hingegen besitzt wieder den in sub2 zugewiesenen Wert 222.

Eine Ausnahme bildet der Skalar $glob. Da diesem Skalar weder mit my noch mit local explizit ein Gültigkeitsbereich zugewiesen wurde, steht er in jedem Anweisungsblock gleichermaßen zur Verfügung. Der Gültigkeitsbereich ist sozusagen global.

# Parameter übergeben

Da die bisherige Verwendung der Subroutinen nicht gerade optimal gewesen ist, werde ich Ihnen nun erklären, wie Sie Parameter an eine Subroutine übergeben können. Dies hat zwei Vorteile:

  1. Variablen, die für das Ausführen der Subroutinen wichtig sind, müssen nicht global definiert werden, und
  2. die einmal geschriebenen Subroutinen sind vielseitig einsetzbar, z. B. in späteren Programmen, da sie nicht auf bestimmte Variablen festgelegt sind.

Unser vorläufiges Ziel ist es, eine Subroutine mit dem Namen html_format zu schreiben, an die Sie mit Parametern zwei Werte übergeben. Der erste Wert soll der Name eines HTML-Elements sein und der zweite Wert der Text, der innerhalb dieses Elements dargestellt werden soll.

Während in JavaScript die zu erwartenden Parameter bei der Funktionsdefinition festgelegt werden müssen, ist dies bei Perl nicht erforderlich. Deshalb gestaltet sich die Entgegennahme von Parametern auch grundlegend anders.

Die Übergabe von Parametern erfolgt beim Aufruf der Subroutine. Die Parameter werden, durch Kommata getrennt, innerhalb der runden Klammern notiert. Für das Unterprogramm html_format könnte ein entsprechender Aufruf folgendermaßen aussehen:

html_format("h1","Hallo WorldWideWeb!");

Nach einem solchen Aufruf stehen die Parameter in einer Liste mit dem Bezeichner @_ der aufgerufenen Routine zur Verfügung. Die Reihenfolge der Parameter in der Liste entspricht dabei der Reihenfolge, in der sie übergeben wurden. Als Erstes wurde der Wert "h1" und dann der Wert "Hallo WorldWideWeb!" übergeben. Mit dem Skalar $_\[\] und der entsprechenden Indexnummer können Sie nun diese Parameter auslesen. Das zu verwendende HTML-Element besitzt den Index 0 und der auszugebende Text den Index 1. Bis zu diesem Zeitpunkt sieht die Subroutine wie folgt aus:

sub html_format  
{  
  my $element = $_[0];  
  my $text = $_[1];  
}

Die abschließende Ausgabe der Parameter erfolgt dann über eine print-Anweisung, die Sie in die Subroutine html_format am Ende einfügen.

sub html_format  
{  
  my $element = $_[0];  
  my $text = $_[1];  
  print "<$element>$text</$element>";  
}

# Elegantere Methode

Eine elegantere Methode zum Auslesen der Parameter ist die Verwendung der Funktion shift. Diese Funktion löscht das erste Element einer Liste und gibt dieses zurück. Dabei rutschen die anderen Elemente der Liste nach vorne. Aus dem zweiten Element wird das erste Element, aus dem dritten das zweite usw. Die Liste, auf die diese Funktion angewendet werden soll, wird normalerweise als Parameter übergeben. Wird jedoch keine Liste angegeben, geht die Funktion davon aus, dass Sie das erste Element der Liste @_ löschen möchten. Und diese Liste enthält schließlich die Parameter, die einer Subroutine übergeben wurden.

Die Anpassung der Subroutine html_format mit Hilfe der shift-Funktion sieht dann wie folgt aus:

sub html_format  
{  
  my $element = shift;  
  my $text = shift;  
  print "<$element>$text</$element>";  
}

Es bleibt Ihnen überlassen, ob Sie die Parameter-Liste @_ trotzdem mit angeben möchten. Ersetzen Sie shift dann einfach durch shift(@_). Schlussendlich stellt sich immer nur die Frage: Was ist übersichtlicher?

# Rückgabewerte

Sie wissen nun, dass Sie an Subroutinen beliebig viele Parameter übergeben können und wie Sie auf diese Parameter mit unterschiedlichen Herangehensweisen zugreifen können. Jedoch können Subroutinen auch Werte zurückgeben. Die entsprechende Anweisung lautet return, gefolgt von der Variablen, die den Wert enthält, der zurückgegeben werden soll. Es ist natürlich auch möglich, konstante Werte zurückgeben zu lassen. Dies wird aber eher selten der Fall sein. Die genaue Syntax lautet:

return mixed;

Anstelle von mixed notieren Sie dann einfach die entsprechende Variable.

Ein Beispiel: Gehen Sie einmal davon aus, dass Sie eine Subroutine schreiben möchten, die Ihnen den Zinseszins errechnet. An diese Subroutine übergeben Sie drei Werte in der Reihenfolge »Kapital«, »Berechnungszeitraum« und »Zinssatz«. Als Wert soll die Subroutine das Kapital nach der angegebenen Dauer mit dem übergebenen Zinssatz zurückgegeben. Die mathematische Formel lautet:

kn = k0 × qn

Diese Formel können Sie natürlich nicht so in Ihr Perl-Script einsetzen. Sie müssen die Formel daher in eine programmtypische Form umschreiben. Zum Verständnis: Kn ist das Kapital nach n Jahren, und q ist der Zinsfaktor. Letzterer errechnet sich wie folgt:

q = 1 + (1 / 100)

Die gesamte Formel zum Berechnen des Zinseszins, die Sie in Perl notieren können, lautet dann:

$Kn = $K * (((100 + $P) / 100) ** $N);

Sie müssen in der Subroutine nun also vier Variablen definieren:

  • $K für das Startkapital
  • $P für den Prozentsatz
  • $N für die Dauer
  • $Kn für das Endkapital

Das ergibt die folgende Subroutine:

sub zinseszins  
{  
  my $K = shift;  
  my $N = shift;  
  my $P = shift;  
  my $Kn;  
  $Kn = $K * (((100 + $P) / 100) ** $N);  
  return $Kn;  
}

Der Beispielaufruf für diese Routine könnte

print zinseszins(1000,2,10);

lauten, was folgende Ausgabe erzeugen würde:

1210

Das Startkapital beträgt 1000. Die Dauer, für die der Zinseszins errechnet werden soll, beträgt 2 Jahre. Der Zinssatz liegt bei 10  %.

# Zusammenfassung

  • Subroutinen sind Unterprogramme in einem Perl-Script.
  • Sie werden durch die Anweisung sub und gefolgt von einem Bezeichner definiert. In runden Klammern folgen die Anweisungen, die bei Aufruf der Subroutine auszuführen sind.
  • An Subroutinen können Parameter übergeben werden, die in der Liste @_ in der Routine zur Verfügung stehen.
  • local und my definieren die Gültigkeitsbereiche von Variablen.
  • Subroutinen können mit der Anweisung return einen Wert zurückgeben.

# Fragen und Übungen

  1. An welcher Stelle in einem Perl-Script dürfen Subroutinen definiert werden?
  2. Welche Regeln müssen bei der Bezeichnung von Subroutinen eingehalten werden?
  3. Worin besteht der Unterschied bei der Variablendefinition innerhalb einer Subroutine mit local oder my ?
  4. Welcher besondere Effekt tritt ein, wenn eine Variable mit my im Hauptprogramm definiert wird?
  5. Es gibt zwei Varianten, um auf die übergebenen Parameter einer Subroutine zuzugreifen. Schreiben Sie dazu jeweils ein Beispiel.