SELFHTML: Perl / Perl-Sprachelemente / Sprungbefehle
Author: Redaktion SELFHTML, selfhtml81@selfhtml.org
Description: Welche Sprungbefehle es in Perl gibt, und welche Auswirkungen sie auf den Programmablauf haben
Keywords: SELFHTML, Perl, Sprungbefehle, goto, next, continue, last, redo, Schleifen
Date: 2005-11-09T00:29:27+01:00

| # Sprungbefehle
| goto next continue last redo

## goto

Der Sprungbefehl `goto`, der es erlaubt, jederzeit an eine beliebige andere, bestimmbare Stelle im Script-Ablauf zu springen, ist längst gebrandmarkt als Inbegriff schlechter Programmierung und Spaghetti-Code-Verursacher. Er ist im Normalfall auch durch ordentlich programmierte Schleifen oder gut strukturierte bedingte Anweisungen vermeidbar.

### Beispiel eines vollständigen CGI-Scripts in Perl:

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";

    print "<h1>Hallo User</h1>\n";
    goto MITTEILUNG;

    AUFMUNTERUNG:
    print "<p>Das Wetter ist ja so sch&ouml;n, dass man etwas unternehmen kann</p>";
    goto ENDE;

    MITTEILUNG:
    print "<p>Dieser Service ist zur Zeit nicht verf&uuml;gbar</p>\n";
    goto AUFMUNTERUNG;

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

### Erläuterung:

Das Script gibt HTML-Code zurück. Um herauszufinden, welche `print`-Anweisungen in welcher Reihenfolge ausgegeben werden, müssen Sie den `goto`-Anweisungen folgen. Bei jeder `goto`-Anweisung wird eine Sprungmarke, ein so genanntes Label angegeben. Das sind im Beispiel die großgeschriebenen Namen. Unter diesem Namen muss das Label irgendwo im Script existieren, und zwar mit abschließendem Doppelpunkt. Anschließend geht es dann mit der Ausführung des Scripts weiter, wenn das Label angesprungen wird.

## next

Der Sprungbefehl `next` ist zur Verwendung innerhalb von Schleifen gedacht. Der aktuelle Schleifendurchgang wird abgebrochen. Der nächste Schleifendurchgang wird gestartet, und die Schleifenbedingung wird dabei neu ausgewertet.

### Beispiel eines vollständigen CGI-Scripts in Perl:

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 $Schluessel;
    my $Wert;
    while (($Schluessel, $Wert) = each(%ENV)) {
      next unless $Schluessel =~ /^HTTP_.*/;
      print "<b>$Schluessel</b> hat den Wert <b>$Wert</b><br>\n";
    }

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

### Erläuterung:

Das Beispiel-Script gibt CGI-Umgebungsvariablen aus, jedoch nur solche, die mit `HTTP_` beginnen. Dazu liest das Script den vordefinierten Hash `%ENV` in einer Schleife für Hashes ein. Mit `next` und nachgestellter bedingter Anweisung sowie einem entsprechenden regulären Ausdruck `/^HTTP_.*/` als Bedingung, die mit `unless` verneint wird, erreicht die Anweisung, dass der nachfolgende `print`-Befehl nicht ausgeführt wird, wenn die aktuelle Umgebungsvariable nicht mit `HTTP_` beginnt. Auf diese Weise werden also nur solche Variablen ausgegeben, bei denen das der Fall ist.

## continue

`continue` gehört genaugenommen nicht zu den Sprungbefehlen, ist aber eng mit diesen verknüpft, da `continue` angesprungen wird. Das Schlüsselwort leitet einen eigenen Anweisungsblock ein. Innerhalb dieses Anweisungsblocks, der wie üblich mit geschweiften Klammern `{` und `}` markiert wird, können Sie beliebige Anweisungen notieren. Wenn ein solcher `continue`-Block unmittelbar hinter einem Schleifenblock steht, wird er mit jedem Schleifendurchlauf ebenfalls durchlaufen und dann (außer beim allerersten Durchlauf) direkt vor der Prüfung der Schleifenbedingung ausgeführt.

### Beispiel eines vollständigen CGI-Scripts in Perl:

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 $i = 1;
    while($i <= 100) {
      next if ($i % 7 != 0);
      print "$i ist durch 7 teilbar<br>\n";
    }
    continue {
         $i++;
    }

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

### Erläuterung:

Das Beispiel gibt alle Zahlen zwischen 1 und 100 aus, die durch 7 teilbar sind. Dabei wird eine `while`-Schleife verwendet, deren Bedingung abfragt, ob `$i` kleiner gleich `100` ist. Da `$i` mit `1` initialisiert wird, wird der Schleifenblock also erreicht. Innerhalb des Schleifenblocks wird `$i` jedoch nicht hochgezählt, was zu einer hoffnungslosen Endlosschleife führen würde. Doch dazu dient im Beispiel der `continue`-Block, der nach der abschließenden geschweiften Klammer des Schleifenblocks folgt. Innerhalb des `continue`-Blocks wird `$i` hochgezählt.

Innerhalb der Schleife wird mit der Bedingung `$i % 7 != 0` abgefragt, ob der aktuelle Wert von `$i` durch 7 geteilt den Rest 0 ergibt (siehe Modulo-Division bei den Berechnungsoperatoren). Durch `next` wird zwar sofort der nächste Schleifendurchgang gestartet, wenn die Zahl nicht ohne Rest durch 7 teilbar ist, doch die Anweisung aus dem `continue`-Block wird in jedem Fall noch ausgeführt.

## last

Der Sprungbefehl `last` bricht eine Schleife sofort ab.

### Beispiel eines vollständigen CGI-Scripts in Perl:

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 $jetzt;
    while(1) {
      $jetzt = time;
      last if ($jetzt % 2 == 0);
    }
    print "$jetzt \n";

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

### Erläuterung:

Das Beispiel-Script ermittelt mit der Funktion time innerhalb einer klassischen Endlosschleife, formuliert mit `while(1)` ("Bedingung ist immer wahr") den aktuellen Zeitwert in Sekunden und speichert ihn in dem Skalar `$jetzt`. Mit der Bedingung `$i % 2 == 0` wird abgefragt, ob der aktuelle Wert von `$jetzt` durch 2 geteilt den Rest 0 ergibt (siehe Modulo-Division bei den Berechnungsoperatoren). Ist diese Bedingung der nachgestellten `if`-Anweisung erfüllt, wird der voranstehende `last`-Befehl ausgeführt. Die Schleife wird dann abgebrochen. Falls das Script also zu einem Zeitpunkt mit ungeradem Sekundenwert aufgerufen wird, wird so oft der aktuelle Sekundenwert ausgegeben, bis `time` den nächsthöheren Sekundenwert zurückliefert.

## redo

Der Sprungbefehl `redo` wiederholt den aktuellen Schleifendurchlauf einfach noch einmal. Dabei wird die Schleifenbedingung nicht noch einmal ausgewertet.

### Beispiel der Textdatei text.txt:

    das ist \
    eine Datei mit einer Konvention.
    Ein Backslash am Ende \
    einer Zeile bedeutet: \
    keine neue Zeile!

### Beispiel eines vollständigen CGI-Scripts in Perl:

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);

    open(FH,"<text.txt");
    my @Zeilen;
    while(<FH>) {
      chomp;
      if(s/\\$//) {
       $_ .= <FH>;
       redo unless(eof(FH));
      }
      push(@Zeilen,$_."\n");
    }
    close(FH);

    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><pre>\n";
    print @Zeilen;
    print "</pre></body></html>\n";

### Erläuterung:

Das Beispiel zeigt eine klassische Verwendung von `redo`. Dabei geht es darum, mehrere Zeilen aufgrund einer Konvention zu einer zusammenzufassen. Die zuvor gezeigte Textdatei enthält zwar fünf Zeilen, aber am Ende sollen daraus nur zwei Zeilen gemacht werden. Zeilen, die mit Backslash enden, sollen in der nächsten Zeile weitergehen.

Das Script öffnet die Textdatei mit der Funktion open. In einer `while`-Schleife liest es die jeweils nächste Zeile der Datei ein. Die Schleifenbedingung ist dabei `<FH>`. Dies bedeutet: nächste Portion aus der mit dem Datei-Handle `FH` verbundenen Datei, und zwar im skalaren Kontext. In diesem Kontext wird dann die nächste Zeile der Datei geliefert.

Innerhalb der Schleife wird mit der Funktion chomp erst einmal das abschließende Zeilenumbruch-Zeichen der aktuellen Zeile entfernt. Dann wird mit dem regulären Ausdruck `s/\\$//` ein eventuell vorhandener Backslash am Ende der Zeile gesucht und ersetzt. Falls tatsächlich ein Backslash gefunden und ersetzt wurde, ist zugleich die Bedingung für das `if` wahr, und es wird der davon abhängige Anweisungsblock ausgeführt. In diesem Block wird mit `$_ .= <FH>;` die nächste Zeile eingelesen und an die aktuelle angehängt. Benutzt wird dabei die vordefinierte Variable `$_` und der Operator für Zeichenkettenverknüpfung (`.`). Anschließend wird mit `redo` wieder an den Beginn der Schleife gesprungen, sofern nicht (`unless`) das Dateiende ( eof) erreicht ist.

`redo` bewirkt, dass die Schleifenbedingung nicht neu ausgewertet wird. Es wird also keine nächste Zeile eingelesen. Die Variable `$_`, auf die sich sowohl die Anweisung `chomp;` als auch der reguläre Ausdruck beziehen, wurde ja schon innerhalb des `if`-Blocks mit einem neuen Wert versorgt. Zum Verständnis: mit `chomp;` ist so viel gemeint wie: `chomp($_);`, und mit `if(s/\\$//)` so viel wie: `if($_ =~ s/\\$//)`. Sowohl das Entfernen des abschließenden Zeilenumbruchs als auch das Bewerten, ob ein abschließender Backslash vorkommt, bezieht sich also implizit auf die vordefinierte Variable `$_`.

Wenn im Beispiel die `if`-Bedingung nicht mehr wahr ist, gelangt das Script dorthin, wo mit der Funktion push die aktuelle Zeile dem Array `@Zeilen` hinzugefügt wird. Darin sind am Ende nur die beiden Zeilen der Datei gespeichert, die per Konvention übrig bleiben sollen. Das Script gibt den Inhalt von `@Zeilen` am Ende aus.

| Operatoren
| Schleifen

© 2007 Impressum