Perl-Programmierung: Kontrollstrukturen

Aus Wikibooks


Fallunterscheidungen / Verzweigungen[Bearbeiten]

Oft 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.

  • Das erste Programm prüft, ob die Eingabe 0 ist. Auf wieviele verschiedene Arte können Sie 0 eingeben?
  • Geben Sie beim ersten Programm 0 but true ein. Wie erklären Sie sich die Ausgabe?
  • Was passiert, wenn Sie keine Zahl eingeben? Schlagen Sie die Fehlermeldungen auf der Handbuchseite perldiag nach!
  • Die Eingabe an ihr Programm kann interaktiv erfolgen oder von einem anderen Programm stammen. Formulieren Sie auf der Kommandozeile eine Befehlsfolge, die die Ausgabe eines Programms ihrem Programm als Eingabe zur Verfügung stellt.
  • Was passiert, wenn Sie oder das andere Programm keine Daten eingeben? Schlagen Sie die Fehlermeldungen auf der Handbuchseite perldiag nach!
  • Geben Sie beim zweiten Programm nicht ganze Zahlen ein. Wie erklären Sie sich die Ausgabe?
  • Schreiben Sie ein Programm, das überprüft, ob die Eingabe positiv, negativ oder 0 ist.

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[Bearbeiten]

Ein 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[Bearbeiten]

 unless(BEDINGUNG)
 {
   ANWEISUNGSBLOCK
 }
 else
 {
   ANWEISUNGSBLOCK
 }

Dies entspricht einer Verwendung von "if(not BEDINGUNG)".

abgekürzte einfache Fallunterscheidung[Bearbeiten]

Aus 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[Bearbeiten]

Schleife mit Laufbedingung[Bearbeiten]

 while(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[Bearbeiten]

 until(BEDINGUNG)
 {
   ANWEISUNGSBLOCK
 }

und analog die Kurzform:

 ANWEISUNG until BEDINGUNG;

Schleife mit Laufvariable[Bearbeiten]

 for(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[Bearbeiten]

 foreach 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[Bearbeiten]

redo[Bearbeiten]

redo 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[Bearbeiten]

last[Bearbeiten]

Die 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[Bearbeiten]

Dieses 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[Bearbeiten]

exit[Bearbeiten]

Die 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[Bearbeiten]

Die 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[Bearbeiten]

Die 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 ).