SELFHTML

Schleifen

Informationsseite

nach unten for-Schleifen
nach unten foreach-Schleifen
nach unten while-Schleifen
nach unten do-Schleifen
nach unten Schleifen bei Hashes
nach unten Rekursion

 nach unten 

for-Schleifen

Solche Schleifen eignen sich vor allem f�r F�lle, in denen es einen Anfangswert, einen Endwert und einen Iterationswert gibt, also beispielsweise "Jede Zahl zwischen 1 und 100".

Beispiel eines vollst�ndigen CGI-Scripts in Perl:

Beispiel-Seite Anzeigebeispiel: So sieht's aus (Zum Aufruf des Scripts ist eine Internet-Verbindung erforderlich)

#!/usr/bin/perl -w

use strict;
use CGI::Carp qw(fatalsToBrowser);

print "Content-type: text/html\n\n";
print '<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">', "\n";
print "<html><head><title>Testausgabe</title>\n";
print "</head><body>\n";

for(my $i = 1; $i <= 100; $i++) {
 print "<span style=\"font-size:$i\".\"pt\">$i pt</span><br>\n";
}

print "</body></html>\n";

Erl�uterung:

Das Script gibt einen Text insgesamt 100 mal aus. Dazu werden hinter dem Schl�sselwort for, das eine for-Schleife einleitet, in Klammern insgesamt drei kleine Anweisungen notiert. Die erste Anweisung deklariert eine Z�hlervariable $i und initiiert sie mit dem Wert 1. Die zweite Anweisung ist eine Bedingung. Sie lautet: "$i kleiner gleich 100". Die dritte Anweisung z�hlt zum aktuellen Wert von $i 1 dazu. Die Schleife wird nun so oft durchlaufen, wie die Bedingung in der Mitte wahr ist. Da $i im Beispiel zun�chst den Wert 1 hat und dann bei jedem Schleifendurchlauf wegen $i++ um 1 erh�ht wird, wird die Schleife insgesamt 100 mal durchlaufen. Beim 101. mal ist $i h�her als 100, die Bedingung ist nicht mehr wahr, und die Schleife wird beendet.

Im Anschluss an das for-Konstrukt folgt ein Seite Anweisungsblock, markiert wie �blich durch geschweifte Klammern { und }. Dazwischen k�nnen beliebig viele Anweisungen stehen. Diese Anweisungen werden so oft ausgef�hrt, wie die Schleife durchlaufen wird, im Beispiel also 100 mal. Im Beispiel wird mit print HTML-Code erzeugt. Dieser enth�lt in einem span-Element eine CSS-Formatdefinition zur Schriftgr��e (font-size). Bei der Zuweisung an diese CSS-Eigenschaft wird der Skalar $i verwendet. Das bewirkt im Beispiel, dass der Text 100 mal ausgegeben wird, und zwar jedesmal mit einer etwas gr��eren Schrift. Die erste ausgegebene Zeile ist nur 1pt, also ein Punkt gro�, was wohl kaum jemand wird lesen k�nnen. Jede ausgegebene Zeile wird aber um einen Punkt gr��er, und die letzte Zeile ist mit 100pt Gr��e schon recht fensterf�llend.

Um Bedingungen wie die in der zweiten Anweisung im Konstrukt der for-Schleife zu formulieren, brauchen Sie entweder zwei Werte, die Sie vergleichen m�chten, oder Sie fragen direkt, ob ein in den Klammern stehender Ausdruck wahr oder falsch ist. Im Beispiel werden in der Bedingung zwei Werte verglichen, n�mlich der Wert von $i mit der Zahl 100. Dazu brauchen Sie Seite Vergleichsoperatoren wie im Beispiel den Kleiner-Als-Operator <.

nach obennach unten

foreach-Schleifen

Diese Sorte Schleifen ist in Perl speziell f�r das Durchlaufen von Seite Listen und Arrays gedacht. Eine Liste wird dabei Element f�r Element abgeklappert. Abh�ngig davon k�nnen Sie Anweisungen ausf�hren.

Beispiel eines vollst�ndigen CGI-Scripts in Perl:

Beispiel-Seite Anzeigebeispiel: So sieht's aus (Zum Aufruf des Scripts ist eine Internet-Verbindung erforderlich)

#!/usr/bin/perl -w

use strict;
use CGI::Carp qw(fatalsToBrowser);

print "Content-type: text/html\n\n";
print '<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">', "\n";
print "<html><head><title>Testausgabe</title>\n";
print "</head><body>\n";


my @Sachen = ("mein Haus","mein Auto","mein Boot");
foreach (@Sachen) {
 print "$_<br>\n";
}

my @Schwaechen = ("Nikotin","Alkohol","das andere Geschlecht");
my $Schwaeche;
foreach $Schwaeche (@Schwaechen) {
 print "$Schwaeche<br>\n";
}

print "</body></html>\n";

Erl�uterung:

Das Beispiel zeigt zwei leicht abweichende Varianten, mit einer foreach-Schleife umzugehen. In beiden F�llen wird jeweils ein Array deklariert und mit Anfangswerten versehen, einmal @Sachen und einmal @Schwaechen. Hinter dem Schl�sselwort foreach wird in Klammern einfach der Array angegeben. In dem Anweisungsblock, der dahinter in geschweiften Klammern folgt, k�nnen beliebig viele Anweisungen stehen.

Im ersten der obigen Beispiele wird von der Seite vordefinierten Variablen $_ Gebrauch gemacht. In ihr ist im Anweisungsblock einer foreach-Schleife stets der aktuelle Wert des Schleifendurchlaufs gespeichert, was in diesem Fall das jeweils aktuelle Element des Arrays @Sachen ist.

Im zweiten Beispiel wird anstelle von $_ ein eigener Skalar namens $Schwaeche benutzt. Wenn ein solcher Skalar zwischen dem Schl�sselwort foreach und der Klammer mit dem Array notiert wird, ist im Anweisungsblock in diesem Skalar jeweils der aktuelle Wert des Schleifendurchlaufs enthalten, im zweiten Beispiel also der jeweils aktuelle Wert aus @Schwaechen.

Beachten Sie:

Die Schl�sselw�rter for und foreach besitzen zwar jeweils einen semantisch anderen Hintergrund, sind aber syntaktisch beliebig gegeneinander austauschbar. Perl erkennt selbst�ndig, was f�r einen Typ Schleife Sie verwenden wollen. So k�nnen Sie beispielsweise auch folgendes schreiben:
for(1..1000) {
 print "tausendmal ber�hrt\n";
}

Der Code gibt einfach tausendmal den Text aus, ist aber im Grunde eine foreach-Schleife, welche die Liste der Zahlen von 1 bis 1000 abarbeitet.

nach obennach unten

while-Schleifen

Diese Art von Schleifen eignet sich, wenn Sie vorher nicht wissen, wie oft die Schleife durchlaufen wird. Sie formulieren einfach eine Bedingung, und die Schleife wird so oft durchlaufen, wie die Bedingung wahr ist. Dass die Bedingung irgendwann falsch und die Schleife beendet wird, daf�r m�ssen Sie im Anweisungsblock, der von der Schleife abh�ngig ist, selber sorgen.

Beispiel eines vollst�ndigen CGI-Scripts in Perl:

Beispiel-Seite Anzeigebeispiel: So sieht's aus (Zum Aufruf des Scripts ist eine Internet-Verbindung erforderlich)

#!/usr/bin/perl -w

use strict;
use CGI::Carp qw(fatalsToBrowser);

print "Content-type: text/html\n\n";
print '<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">', "\n";
print "<html><head><title>Testausgabe</title>\n";
print "</head><body>\n";

my $Startzeit = time();
my $Endzeit = $Startzeit + 1;
my $Jetztzeit = 0;
my $i = 0;

while ($Jetztzeit <= $Endzeit) {
 $Jetztzeit = time();
 $i++;
}
print "$i mal durchlaufen<br>";

print "</body></html>\n";

Erl�uterung:

Das Script ermittelt zun�chst mit der Perl-Funktion Seite time den aktuellen Zeitpunkt und speichert das Ergebnis im Skalar $Startzeit. Gespeichert wird dabei eine Zahl, n�mlich die Anzahl Sekunden vom 1.1.1970, 0.00 Uhr bis zum aktuellen Zeitpunkt. Dann wird ein Skalar $Endzeit deklariert, der einen Wert zugewiesen bekommt, der um 1 h�her ist als der von $Startzeit. Zwei weitere Skalare $Jetztzeit und $i werden deklariert und mit 0 initialisiert.

Die Schleife wird durch das Schl�sselwort while eingeleitet. In Klammern wird eine Bedingung formuliert. Im Beispiel wird die Bedingung "$Jetztzeit kleiner oder gleich $Endzeit" formuliert. Hinter der Bedingung folgt in geschweiften Klammern ein Anweisungsblock mit beliebig vielen Anweisungen. Ausgef�hrt werden diese Anweisungen so oft, wie die Schleife durchlaufen wird und die Bedingung noch wahr ist.

Die Schleifenbedingung ist im Beispiel ja zun�chst auf jeden Fall wahr, da $Jetztzeit mit 0 initialisiert wurde und daher auf jeden Fall kleiner ist als $Endzeit. Innerhalb der Schleife bekommt $Jetztzeit jedoch durch Aufrufen der Funktion time einen neuen Wert zugewiesen, der logischerweise mindestens so hoch ist wie der von $Startzeit. Die Schleife wird dadurch so lange durchlaufen, bis $Jetztzeit durch den time-Aufruf einen Wert zugewiesen bekommt, der gr��er ist als $Endzeit. Dann wird die Schleife beendet. Wie oft das der Fall ist, wissen Sie nat�rlich vorher nicht, insofern ist die while-Schleife hier ideal.

Innerhalb der Schleife wird au�erdem noch $i als Z�hlervariable mit $i++ jeweils um 1 erh�ht. Der aktuelle Wert von $i wird nach Ablauf der Schleife ausgegeben. Im Fenster des aufrufenden Browsers wird man also am Ende sehen k�nnen, wie oft die Schleife durchlaufen wurde.

nach obennach unten

do-Schleifen

Bei while-Schleifen kann es passieren, dass die abh�ngigen Anweisungen nie ausgef�hrt werden, n�mlich dann, wenn die Schleifenbedingung schon beim ersten Schleifendurchlauf unwahr ist. Eine do-Schleife sorgt daf�r, dass die Anweisungen auf jeden Fall einmal ausgef�hrt werden, da die Bedingung der Schleife erst am Ende abgepr�ft wird.

Beispiel eines vollst�ndigen CGI-Scripts in Perl:

Beispiel-Seite Anzeigebeispiel: So sieht's aus (Zum Aufruf des Scripts ist eine Internet-Verbindung erforderlich)

#!/usr/bin/perl -w

use strict;
use CGI::Carp qw(fatalsToBrowser);

print "Content-type: text/html\n\n";
print '<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">', "\n";
print "<html><head><title>Testausgabe</title>\n";
print "</head><body>\n";

my $Bedingung = "Abbruch";
my $Irgendwas;
do {
 $Irgendwas = $Bedingung;
 print "Hier steht $Irgendwas";
} until ($Irgendwas eq $Bedingung);

print "</body></html>\n";

Erl�uterung:

Das Beispiel demonstriert die typische Funktionsweise einer solchen Schleife. Zun�chst wird ein Skalar $Bedingung mit dem Anfangswert Abbruch versehen. Ein weiterer Skalar namens $Irgendwas wird deklariert, erh�lt aber keinen Wert. Die Schleife wird mit do eingeleitet. Dahinter folgt in geschweiften Klammern ein Anweisungsblock, der beliebig viele Anweisungen enthalten kann. Im Beispiel wird dem Skalar $Irgendwas gleich zu Beginn der Wert von $Bedingung zugewiesen, also Abbruch. Anschlie�end wird dieser Inhalt zur Kontrolle ausgegeben. Nach der schlie�enden geschweiften Klammer, die den Anweisungsblock beendet, ist das Wort until notiert und dahinter in Klammern die eigentliche Schleifenbedingung. Im Beispiel wird abgepr�ft, ob $Irgendwas und $Bedingung gleich sind, also den gleichen Inhalt haben. Da dies ja innerhalb der Schleife zugewiesen wurde, ist die Schleifenbedingung also erf�llt. Damit wird die Schleife abgebrochen. Denn until ist wie "solange bis" zu lesen. Im Gegensatz zur while-Schleife, deren Anweisungsblock ausgef�hrt wird, solange die Bedingung wahr ist, wird hier der Anweisungsblock ausgef�hrt, bis die Schleifenbedingung wahr ist.

Im Beispiel wird die Schleife einmal durchlaufen, obwohl die Schleifenbedingung gleich im ersten Durchlauf wahr ist. Der Grund ist eben, dass zuerst der abh�ngige Code ausgef�hrt und erst dann die Bedingung �berpr�ft wird.

Beachten Sie:

do-Schleifen sind eigentlich keine echten Schleifen, weshalb dort Sprungbefehle wie last, next und redo nicht funktionieren.

Es gibt in Perl auch do-Schleifen, deren Bedingung kein until, sondern ein while vorangestellt ist. Dann m�ssen Sie die Schleifenbedingung negativ formulieren.

nach obennach unten

Schleifen bei Hashes

So wie sich Arrays prima mit nach oben foreach-Schleifen "traversieren", also Element f�r Element durchlaufen lassen, besteht dieser Wunsch nat�rlich auch bei Seite Hashes. Da ein Hash-Element jedoch immer aus zwei Werten besteht, von denen der erste der Schl�ssel ist und der zweite der eigentliche Datenwert, ist ein einfaches Traversieren wie mit foreach nicht m�glich. Deshalb gibt es f�r Hashes eine eigene Schleifen-Syntax.

Beispiel eines vollst�ndigen CGI-Scripts in Perl:

Beispiel-Seite Anzeigebeispiel: So sieht's aus (Zum Aufruf des Scripts ist eine Internet-Verbindung erforderlich)

#!/usr/bin/perl -w

use strict;
use CGI::Carp qw(fatalsToBrowser);

print "Content-type: text/html\n\n";
print '<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">', "\n";
print "<html><head><title>Testausgabe</title>\n";
print "</head><body>\n";

my %Familie = (Frau => "Eva", Tochter => "Anja", Sohn => "Florian");
my $Schluessel;
my $Wert;

while (($Schluessel, $Wert) = each(%Familie)) {
  print "$Schluessel hei&szlig;t $Wert<br>\n";
}

while ($Schluessel = each(%Familie)) {
  print "$Schluessel hei&szlig;t $Familie{$Schluessel}<br>\n";
}

print "</body></html>\n";

Erl�uterung:

Das Beispiel deklariert einen Hash namens %Familie und weist ihm drei Schl�ssel-Wert-Paare zu. Anschlie�end werden zwei Skalare $Schluessel und $Wert deklariert, die innerhalb der Schleife ben�tigt werden. Die Schleife wird als while-Schleife formuliert. Innerhalb der Schleifenbedingung wird jedoch die Perl-Funktion Seite each aufgerufen. Diese liefert wahlweise eine Liste mit zwei Elementen, n�mlich dem jeweils n�chsten Schl�ssel und dem zugeh�rigen Wert, oder - im skalaren Kontext - nur den jeweils n�chsten Schl�ssel des �bergebenen Hashes.

Das Beispiel-Script zeigt beide Varianten. In der ersten Variante wird die Liste mit den beiden Elementen in dem Ausdruck ($Schluessel, $Wert) gespeichert. $Schluessel enth�lt dann den jeweils aktuellen Schl�ssel des Hashs, und $Wert den zugeh�rigen Datenwert. Im Beispiel wird die Schleife dreimal durchlaufen und gibt solche S�tze aus wie Frau hei�t Eva.

In der zweiten Variante wird die each-Funktion im skalaren Kontext aufgerufen, da der R�ckgabewert nur in $Schluessel gespeichert wird. Die innerhalb der Schleife formulierte print-Anweisung gibt daher das Gleiche aus wie in der ersten Variante. Diesmal ist jedoch keine Variable $Wert verf�gbar. �ber ein Konstrukt wie $Familie{$Schluessel} kann aber auf den jeweils aktuellen Wert zugegriffen werden.

nach obennach unten

Rekursion

Rekursion ist dann ein Mittel, wenn man mit Schleifen nicht mehr weiter kommt. Ein typischer Anwendungsfall f�r Rekursion ist das Traversieren von baumartigen Strukturen. Auf gut Deutsch: wenn Sie beispielsweise einen ganzen Verzeichnisbaum einlesen wollen, ohne die Datei- und Verzeichnisstruktur vorher zu kennen, dann ist das ein typischer Fall f�r eine rekursive Anwendung. Bei der Rekursion wird eine Seite Subroutine definiert, innerhalb derer es eine Anweisung gibt, die die Subroutine von neuem aufruft. Dadurch entsteht ein Verschachtelungseffekt. Rekursion ist allerdings aus Computersicht nicht ganz unkritisch. Deshalb muss sie sauber programmiert sein.

Das folgende Beispiel zeigt, wie Sie eine Datei- und Verzeichnisstruktur ab einem gegebenen Startverzeichnis einlesen und an den aufrufenden Browser HTML-formatiert �bermitteln lassen k�nnen. Das Beispiel ist allerdings nicht ganz trivial.

Beispiel eines vollst�ndigen CGI-Scripts in Perl:

Beispiel-Seite Anzeigebeispiel: So sieht's aus (Zum Aufruf des Scripts ist eine Internet-Verbindung erforderlich)

#!/usr/bin/perl -w

use strict;
use CGI::Carp qw(fatalsToBrowser);

my $Startverzeichnis = "/var/www/selfhtml.org/de/dokumente/perl";
my @Alle;
my $Totalbytes = 0;

print "Content-type: text/html\n\n";
print '<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">', "\n";
print "<html><head><title>Testausgabe</title>\n";
print "</head><body>\n";
print "<h1>Dateibaum</h1>\n";
print "<pre>Startverzeichnis: <b>$Startverzeichnis</b></pre>\n";
print "<hr noshade size=\"1\"><pre>\n";

Ermitteln($Startverzeichnis);
@Alle = sort(@Alle);
foreach (@Alle) {
  print "$_\n";
}
print "</pre><hr noshade size=\"1\">\n";
print "<pre>Insgesamt: [$Totalbytes Bytes]</pre>\n";
print "</body></html>\n";


sub Ermitteln {
  my $Verzeichnis = shift;
  my $Eintrag;
  my $Pfadname;
  my $HTML_Eintrag;
  my $Bytes;
  local *DH;

  unless (opendir(DH, $Verzeichnis)) {
   return;
  }
  while (defined ($Eintrag = readdir(DH))) {
   next if($Eintrag eq "." or $Eintrag eq "..");
   $Pfadname = $Verzeichnis."/".$Eintrag;
   if( -d $Pfadname) {
    $HTML_Eintrag = $Verzeichnis."/".$Eintrag." [VERZEICHNIS]";
   }
   else {
    $Bytes = -s $Pfadname;
    $Totalbytes += $Bytes;
    $HTML_Eintrag = $Verzeichnis."/".$Eintrag." [$Bytes Bytes]";
   }
   push(@Alle, $HTML_Eintrag);
   Ermitteln($Pfadname) if(-d $Pfadname);
  }
 closedir(DH);
}

Erl�uterung:

Zun�chst werden drei wichtige Variablen deklariert: $Startverzeichnis speichert das Verzeichnis, ab dem die Suche starten soll, @Alle ist die Liste, in der sp�ter die eingelesenen Eintr�ge gespeichert werden, und $Totalbytes ermittelt die Bytezahlen aller Dateien.

Danach wird mit der HTML-Ausgabe begonnen. Unterhalb davon steht die Anweisung Ermitteln($Startverzeichnis);. Dies ist ein Aufruf der Subroutine Ermitteln, die etwas weiter unten mit sub Ermitteln eingeleitet wird. Diese Subroutine ist zugleich diejenige, die sich in dem Anweisungsblock, den sie einschlie�t, in ihrer vorletzten Anweisung selbst wieder aufruft und so die Rekursion bewirkt.

Mit der Anweisung Ermitteln($Startverzeichnis); passiert damit das gesamte Einlesen der Datei- und Verzeichnisstruktur. Anschlie�end wird die Liste mit der Funktion sort alphabetisch sortiert und dann Eintrag f�r Eintrag aus einer foreach-Schleife heraus ausgegeben.

Das Herzst�ck des Scripts ist die Subroutine Ermitteln. Darin wird zun�chst eine Reihe von Arbeitsvariablen deklariert. Da die Subroutine sich ja selber wieder aufruft, stellt sich die Frage, ob es dabei nicht zu einem Kuddelmuddel mit den Namen der Variablen kommt. Die Antwort ist nein. Denn jedes Ausf�hren der Subroutine erzeugt eine eigene Instanz der Routine im Arbeitsspeicher, und da die Variablen lokal mit my deklariert sind, bleibt ihre G�ltigkeit auf eine Instanz beschr�nkt.
Eine offensichtliche Ausnahme bildet die Anweisung local *DH, die das Verzeichnishandle DH lokal deklariert. Da my nicht auf Datei-/Verzeichnishandles (bzw. Typeglobs) angewendet werden kann, wird hier zu dieser L�sung gegriffen, die intern zwar etwas anders arbeitet, aber den gew�nschten Effekt hat. Eine andere Variante w�re, das Seite Standardmodul Symbol zu verwenden und sich in jeder Instanz der Subroutine ein neues Verzeichnishandle zu schaffen. Das Verfahren mag "sauberer" erscheinen, ist es aber im Endeffekt nicht. Au�erdem ist die Variante mit local bedeutend schneller.
Die vielen Instanzen der Subroutine bei vielen Verzeichnissen f�hren aber auch dazu, dass immer mehr Arbeitsspeicher ben�tigt wird. Das ist ein wichtiger Nachteil der Rekursion. Konstrukte mit vielen rekursiven Selbstaufrufen sollten Sie daher in CGI-Scripts, die auf �ffentlichen Webservern sehr h�ufig und in mehreren Prozessen gleichzeitig aufgerufen werden k�nnen, vermeiden.

Die Subroutine Ermitteln erwartet einen Verzeichnispfadnamen, der ihr �bergeben wird. Mit $Verzeichnis = shift; wird der �bergebene Pfadname im Skalar $Verzeichnis gespeichert (siehe dazu auch die Perl-Funktion Seite shift). Anschlie�end wird mit der Funktion Seite opendir das �bergebene Verzeichnis ge�ffnet. Seine Eintr�ge werden in einer while-Schleife mit der Funktion Seite readdir eingelesen. Die beiden Eintr�ge mit den Werten . und .., die in jedem Verzeichnis vorkommen und das aktuelle bzw. das �bergeordnete Verzeichnis symbolisieren, werden mit dem Sprungbefehl Seite next �bersprungen. Andernfalls w�rde sich die Rekursion in einer Endlosschleife verheddern.

Mit dem Seite Dateitestoperator -d in if( -d $Pfadname) wird abgefragt, ob der jeweils aktuelle Verzeichniseintrag wieder ein Verzeichnis, also ein Unterverzeichnis, ist. Abh�ngig davon wird ein HTML-Eintrag f�r die auszugebende Liste vorbereitet. Weiter unten wird dann mit -d noch einmal abgefragt, ob der Eintrag ein Unterverzeichnis ist, und davon abh�ngig die Subroutine Ermitteln mit dem Unterverzeichnis erneut aufgerufen.

Nachdem die Verzeichnisstruktur abgearbeitet ist und alle Instanzen der Subroutine Ermitteln beendet sind, geht es im oberen Teil des Scripts weiter mit @Alle = sort(@Alle);.

 nach oben
weiter Seite Sprungbefehle
zur�ck Seite Bedingte Anweisungen
 

© 2007 Seite Impressum