Perl-Programmierung: Kontrollstrukturen
Fallunterscheidungen / Verzweigungen
BearbeitenOft werden Programmteile erst ausgeführt, wenn eine bestimmte Bedingung eingetreten ist. Diese Bedingung muss dann zunächst geprüft werden.
Mit den Mitteln, die wir bisher kennengelernt haben, kommen wir nicht zum Ziel. In Perl schaffen hier if
-Konstrukte Abhilfe:
Die einfachste Fallunterscheidung prüft, ob eine Bedingung zutrifft. Dafür wird vor einen Block die if
-Anweisung gesetzt, der die Bedingung in Klammern folgt:
#!/usr/bin/perl
use strict;
use warnings;
# Zur Eingabe auffordern
print "Bitte geben Sie ein Zahl ein:\n";
# Zeile einlesen
my $z = <STDIN>;
# Zeilenumbruch entfernen
chomp $z;
# Ist z 0?
if ( $z == 0 ) {
# ja, z ist 0
print "Die Eingabe ist 0\n";
}
Obiges Programm gibt nur dann etwas aus, wenn tatsächlich 0 eingegeben wird.
Die Bedingung, die hinter der if-Anweisung in Klammern steht, wird im boolschen Kontext ausgewertet, der eine Sonderform des skalaren Kontexts ist. Mehr dazu steht im Abschnitt über Kontexte. |
Wir möchten nun ein Programm schreiben, das überprüft, ob eine ganze Zahl gerade oder ungerade ist und uns dann eine entsprechende Ausgabe liefert.
#!/usr/bin/perl
use strict;
use warnings;
# Zur Eingabe auffordern
print "Bitte geben Sie eine Zahl ein:\n";
# Zeile einlesen
my $z = <STDIN>;
# Zeilenumbruch entfernen
chomp $z;
# Ist z gerade?
if ( $z % 2 == 0 ) {
printf ( "%d ist gerade\n" , $z );
}
# Ist z ungerade?
if ( $z % 2 == 1 ) {
printf ( "%d ist ungerade\n" , $z );
}
Die erste printf-Anweisung wird nur dann ausgeführt, wenn die Bedingung in den runden Klammern nach dem Schlüsselwort if
erfüllt ist. Nur wenn sich $z ohne Rest durch 2 dividieren lässt, wird ausgegeben, dass die Zahl gerade ist. Genauso funktioniert auch die zweite if
-Bedingung. Mithilfe von else
, einem weiteren Schlüsselwort, könnten wir das Programm auch so realisieren:
#!/usr/bin/perl
use strict;
use warnings;
# Zur Eingabe auffordern
print "Bitte geben Sie ein Zahl ein:\n";
# Zeile einlesen
my $z = <STDIN>;
# Zeilenumbruch entfernen
chomp $z;
if ( $z % 2 == 0 ) {
printf ( "%d ist gerade\n" , $z );
} else {
printf ( "%d ist ungerade\n" , $z );
}
Die printf
-Anweisung, die in den geschweiften Klammern hinter else
steht, wird ausgeführt, wenn die Bedingung in der vorherigen if
-Anweisung nicht erfüllt ist. Dies entspricht auch der Bedeutung der beiden Wörter if
und else
in der englischen Sprache: falls (engl. if) die erste Bedingung wahr ist, wird die erste Anweisung ausgeführt, ansonsten (engl. else) wird die zweite Anweisung ausgeführt. Da eine ganze Zahl, die nicht gerade ist, zwangsweise ungerade sein muss, arbeitet das Programm nach wie vor korrekt.
|
In Perl können wir if-else
-Konstrukte beliebig schachteln. Folgendes Beispiel enthält eine if
-Abfrage, die sich in einem Anweisungsblock einer anderen if
-Abfrage befindet:
#!/usr/bin/perl
use strict;
use warnings;
# Zur Eingabe auffordern
print "Bitte geben Sie das Passwort ein:\n";
# Passwort einlesen
my $passwort = <STDIN>;
chomp $passwort;
# Abfragen, ob das Passwort korrekt ist
if ( $passwort eq "topsecret" ) {
# Passwort korrekt
print "Wollen Sie ihren Kontostand abfragen?\n";
my $eingabe = <STDIN>;
chomp $eingabe;
# Gegebenenfalls Kontostand anzeigen
if ( $eingabe eq "ja" ) {
print "Kontostand: 0 Euro\n";
}
} else {
# Passwort falsch
print "Das Passwort war leider nicht korrekt.\n";
}
print "Auf Wiedersehen!\n";
Erst wenn das Passwort korrekt ist, können wir überprüfen, ob der Kontostand angezeigt werden soll. Aus diesem Grund schachteln wir die Fallunterscheidungen. Es empfiehlt sich, auf solche Schachtelungen wenn möglich zu verzichten, um den Quelltext übersichtlich zu gestalten. Hierzu folgendes Beispiel:
#!/usr/bin/perl
use strict;
use warnings;
my $eingabe = 50;
if($eingabe > 42){
if($eingabe % 2 == 0){
if($eingabe != 92){
$eingabe = 0;
}
}
}
Mithilfe der logischen Operatoren, die wir schon kennengelernt haben, lässt sich das Programm übersichtlicher schreiben:
#!/usr/bin/perl
use strict;
use warnings;
my $eingabe = 50;
if($eingabe > 42 && $eingabe % 2 == 0 && $eingabe != 92){
$eingabe = 0;
}
Standardfallunterscheidung
BearbeitenEin allgemeines Wörtchen zu Wahrheit und Falschheit in Perl: Die Ausdrücke 0, undef und alle Ausdrücke/Bedingungen, die dazu evaluiert werden, sind falsch. Alle anderen Ausdrücke/Bedingungen sind wahr.
if(BEDINGUNG1) { ANWEISUNGSBLOCK } elsif(BEDINGUNG2) { ANWEISUNGSBLOCK } elsif(BEDINGUNG3) { ANWEISUNGSBLOCK } else { ANWEISUNGSBLOCK }
Die geschweiften Klammern sind zwingend notwendig! Die einfache Fallunterscheidung ist ein Spezialfall der mehrfachen Fallunterscheidung.
negierte Fallunterscheidung
Bearbeitenunless(BEDINGUNG) { ANWEISUNGSBLOCK } else { ANWEISUNGSBLOCK }
Dies entspricht einer Verwendung von "if
(not BEDINGUNG)".
abgekürzte einfache Fallunterscheidung
BearbeitenAus manchen Programmiersprachen ist ein abgekürztes Verfahren für einfache Fallunterscheidungen mit jeweils genau einer Anweisung je Fall bekannt. Auch Perl bietet vereinfachte Verfahren hierfür an:
Verfahren 1:
BEDINGUNG && ANWEISUNG_WAHR; BEDINGUNG || ANWEISUNG_FALSCH;
Verfahren 2:
ANWEISUNG_WAHR if BEDINGUNG; ANWEISUNG_FALSCH unless BEDINGUNG;
Schleifen
BearbeitenSchleife mit Laufbedingung
Bearbeitenwhile(BEDINGUNG) { ANWEISUNGSBLOCK }
Daneben existiert folgende Form der while
-Schleife. Sie bewirkt, dass der Anweisungsblock mindestens einmal ausgeführt wird, da die Abfrage der Bedingung erst am Ende stattfinden:
do { ANWEISUNGSBLOCK } while(BEDINGUNG);
Es gibt außerdem eine Kurzform:
ANWEISUNG while BEDINGUNG;
Schleife mit Abbruchbedingung
Bearbeitenuntil(BEDINGUNG) { ANWEISUNGSBLOCK }
und analog die Kurzform:
ANWEISUNG until BEDINGUNG;
Schleife mit Laufvariable
Bearbeitenfor(STARTANWEISUNG; LAUFBEDINGUNG; LAUFANWEISUNG) { ANWEISUNGSBLOCK }
In der Regel wird in der Startanweisung die Laufvariable initialisiert, die Laufbedingung enthält einen Grenzwert und vergleicht diesen mit der Laufvariablen und in der Laufanweisung wird die Laufvariable inkrementiert (i.e. um 1 erhöht).
Listenabarbeitungsschleife
Bearbeitenforeach VARIABLE ( LISTENVARIABLE ) { ANWEISUNGSBLOCK }
Bei dieser Schleife kann auf das aktuelle Element des abgearbeiteten Arrays mit $_ zugegriffen werden.
Beispiel:
my @liste = qw( asdf jklö 1 2 3 4.56 );
foreach ( @liste ) {
print "$_" . "\n";
}
Dieses Beispiel würde folgenden Output liefern:
asdf jklö 1 2 3 4.56
Natürlich kann man in gewohnter TIMTOWTDI-Manier auch hier die alternative Schreibform benutzen falls gewünscht:
#!/usr/bin/perl
print for ( 1 .. 9 );
Dieses Stück Code würde, wie erwartet, die Zahlen von 1 bis 9 ausgeben.
Sprunganweisungen
Bearbeitenredo
Bearbeitenredo
wird benutzt um wieder zum Anfang einer Schleife zu springen, ohne dabei die Laufanweisung auszuführen.
#!/usr/bin/perl
use strict;
use warnings;
print 'hallo'."\n";
for (my $i = 1; $i <= 10; $i++) {
print 'nun sind wir vor redo'."\n";
if ($i == 2) {
print 'wir lassen eine Stelle aus und gehen direkt wieder zum Anfang der Schleife'."\n";
redo;
}
print 'nun sind wir nach redo'."\n";
}
Das gibt folgendes aus:
hallo nun sind wir vor redo nun sind wir nach redo nun sind wir vor redo wir lassen eine Stelle aus und gehen direkt wieder zum Anfang der Schleife nun sind wir vor redo wir lassen eine Stelle aus und gehen direkt wieder zum Anfang der Schleife nun sind wir vor redo wir lassen eine Stelle aus und gehen direkt wieder zum Anfang der Schleife nun sind wir vor redo ...
continue
Bearbeitenlast
BearbeitenDie last
-Funktion wird benutzt um eine Schleife anzuhalten und dann im Programmcode fortzufahren.
#!/usr/bin/perl
use strict;
use warnings;
print "hallo\n";
for (my $i = 1; $i <= 100; $i++) {
print '$i: ' . $i . "\n";
if ($i == 3) {
last;
}
}
print 'last wurde ausgefuehrt, die Schleife wurde angehalten und es geht weiter...';
Das wird folgendes ausgeben:
hallo $i: 1 $i: 2 $i: 3 last wurde ausgefuehrt, die Schleife wurde angehalten und es geht weiter...
next
BearbeitenDieses Kommando arbeitet ähnlich wie das bereits beschriebene redo
. Es erfolgt ein Sprung zum Beginn der Schleife.
Im Gegensatz zu redo
, wird jedoch in for
, while
und until
Schleifen die Bedingung mit überprüft. Der Schleifenkopf wird also ausgewertet, als ob die Schleife normal wiederholt würde.
Programmabbrüche
Bearbeitenexit
BearbeitenDie exit
-Funktion beendet sofort die Programmausführung mit einem angegebenen Exit-Status. Diese Funktion wird verwendet, um das Programm bei Fehlern zu beenden und um mithilfe des zurückgegeben Wertes verschiedene Arten von Fehlern zu unterscheiden.
Beispiel:
if(!-e "nix.txt") { # alternativ: unless ( -e "nix.txt" )
print "Datei nicht gefunden!";
exit(1);
}
Hier wird überprüft, ob eine Datei namens nix.txt existiert. Wenn nicht, wird eine Fehlermeldung ausgegeben und das Programm mit dem Wert 1 beendet.
warn
BearbeitenDie warn
-Funktion gibt einen Text sowie die Zeilennummer der betreffenden Zeile im Quelltext auf die Standardfehlerausgabe (STDERR) aus. Diese Funktion wird oft in Kombination mit der exit
-Funktion bei der Fehlersuche benutzt, um Fehlermeldungen in eine log-Datei zu schreiben und das Programm zu beenden.
Beispiel:
if(!-e "nix.txt"){
warn "Datei nicht gefunden!";
exit(1);
}
Hier wird zuerst überprüft, ob eine Datei "nix.txt" existiert. Wenn nicht, wird eine Fehlermeldung ausgegeben und das Programm beendet. Die Ausgabe würde so aussehen:
Datei nicht gefunden! at beispiel.pl line 2.
Die unten beschriebene Funktion die
ist eine vereinfachte Kombination der Befehle warn
und exit
.
die
BearbeitenDie die
-Funktion gibt die Fehlermeldung, die ihr übergeben wird auf der Standardfehlerausgabe ( STDERR ) aus und beendet das Programm mit einem Exit-Status ungleich 0. Dies kann praktisch sein, um schwere Fehler im Vorhinein abzufangen ( zB wenn eine notwendige Datei nicht geöffnet werden kann ).
Beispiel:
open LOGDATEI, '>', '/tmp/log_mein_prog' or die "Kann Log-Datei nicht oeffnen: $!\n";
In diesem Beispiel wird versucht, die Logdatei "/tmp/log_mein_prog" zum Schreiben ( > ) zu oeffnen. Falls dies fehlschlägt, wird das Programm mit die und der mitgegebenen Meldung beendet. Die Spezialvariable $! enthält die System-Fehlermeldung in lesbarem Format ( zB. permission denied ).