You are here: Wissensbasis Web>PerlTafelKapitel5?>Perl6TafelAblaufstrukturen (2009-12-03)

Übersicht || 0:Vor | 1:Ge | 2:Theo | 3:Var | 4:Op | 5: Ein- und Ausgabe | 6: Ablaufstrukturen | 7: Subroutinen | 8:OOP | 9:Rx || A:Index | B:Tab | C:Tour | D:Delta | E:Links

Einmalige Ausführung

Blöcke

Geschweifte Klammern umschließen nun immer einen Block. (Nur wenn sie Hashkonstruktoren ("=>") enthalten oder ihnen das Schlüsselwort hash vorrangestellt ist, erstellen sie eine Hashreferenz.) Ein Block ist in Perl 6 eine Zusammenfassung von Befehlen (Ausdrücken), die mit ";" getrennt werden, ein Geltungsbereich und möglicher Namensraum für lexikalische Variablen und Routinen (first class closure). Er hat immer eine eigene Kontextvariable $_, ist Lieferant eines Ergebniswertes und letztlich eine Variable vom Typ Codereferenz mit einem Sack voller Traits, die innerhalb des Blocks als Spezial-Unterblöcke definiert werden. Das Prinzip ist schon von den Perl 5-BEGIN-Blöcken bekannt, aber aber zu den fortgeschrittenen Konzepten später.

Wichtig ist es zu verstehen, daß alles Aufgezählte für jeden Block gleichermaßen gilt, egal mit welchem Wort er eingeleitet wird. do, if, given, sub und all diese Befehle, die hier noch vorgestellt werden, erweitern oder verändern das Verhalten eines Blocks. Deshalb nennt der Index (Anhang A) sie hier auch Blockmodifikatoren (block modifier).

do

Der =do=-Befehl, evaluiert den den nachfolgenden Block zu einem Ausdruck. Larry spricht hier auch von einer "do once loop" (Schleife mit einmaliger Ausführung) weswegen "do {...} while (...);" (gültiger Perl 6 Code) jetzt "repeat {} while (...)" geschrieben wird.

Wenn do auf einen einzelnen Befehl angewandt wird, kann man die gescheiften Klammern weglassen. do kann auch mit anderen Block-Modifikatoren kombiniert werden. Dadurch kann man sich die Ergebnisse der Ausführung geben lassen, was dem Rückgabewert des letzten ausgeführten Befehl entspricht. Die Lieferung eines Rückgabewertes mit return, ist den noch vorzustellenden Subroutinen ( sub) vorbehalten.

    do {}                             # 0, Block darf leer bleiben
    do 1 if 2;                        # 1, liefert 1
    $x = do if $a { $b } else { $c }; # wie ein ternärer operator

gather

gather läßt ebenfalls den folgenden Block einmal ausführen, kann jedoch eine Liste von Rückgabewerten liefern. Diese kann man innerhalb des Blockes beliebig mit take kennzeichnen. gather könnte man also mit "jetzt ernte ich" und "take" mit "ich nehm mal das, und dann noch das" übersetzen.

    @maler = gather { 
        take 'Caspar David Friedrich';
        take 'Paul Cezanne', 'Claude Monet';
       'Alfred Sisley';
    }
    say "@maler[]";                     # "Caspar David Friedrich Paul Cezanne Claude Monet"

So richtig interessant wird dieses Konstrukt aber erst, wenn man drumherum eine Schleife baut. Da der Rückgabearray eine Closure (state-Variable) des gather=-Blockes ist, kann man mit jeder Iteration =take weiterverwenden und kann so unabhängig (asynchron) von den Wiederholungen der Schleife mal einen, mal keinen und manchmal mehrere Werte rauspicken.

    my @wahl = gather for @list {       # Beispiel für eine Kombination aus gather und Schleifenbefehl
        take $_ if ...;
    }

gather evaluiert im Gegensatz zu do in den void-Kontext.

leave

Dient dem unverzüglichen verlassen des aktuellen Blocks. Möchte man auch aus einem übergeordneten Block aussteigen, so sage man LABELNAME.leave. Der Befehl bestimmt im Gegensatz zu return keinen Rückgabewert. Er darf dafür auch in Subroutinen ('sub') verwendet werden, return allerding, wie erwähnt nur ebenda.

goto

Der Befehl goto ist kein Blockmodifikator, sondern ein Sprungbefehl der den Ablauf des Programmes zu einer beliebigen Stelle lenken kann, wenn diese mit einem Label gekennzeichnet ist. Ein Label erkennt man am Doppelpunkt nach seinem Namen und auf jede goto Anweisung muss der Name eines in dieser Datei vorhandenen Labels folgen. Durch diese Technik können beliebige Blöcke angesprungen oder verlassen werden. Weil dabei die logische Ablaufstruktur vollständig ignoriert. Deswegen wird dieser Befehl Anfängern nicht empfohlen, manche sind sogar der Ansicht goto sei ein Relikt aus längst vergangenen Tagen als Dinosaurier hier noch lebten und die Menschen noch FORTRAN sprachen. Folgendes Beispiel würde auch in Perl5 laufen, es macht fast dasselbe wie $zaehler=11; nur umständlicher:

    imkreis:                     # Label nr.1
    $zaehler++;                  # $zaehler = $zaehler+1;
    $zaehler > 10 and goto frei; # wenn $zaehler 11 ist springt es zu 2. Label
    goto imkreis;                # springt wieder zum anfang zum 1. Label
    frei:                        # Label nr.2

sub

Statt ein Label vor ein Blockbegin zu setzen und dieses dann mit goto anzuspringen kann man auch mit dem Blockmodifikator sub einem Block einen Namen geben um ihn dann direkt mit diesem Namen auszurufen. Die Möglichkeiten die sich daraus ergeben sind sehr umfangreich und dies ist eines der wichtigsten Programmierelemente überhaupt. Deswegen dient das gesamte, folgende Tafel 6 dazu, die Möglichkeiten dieses Befehls zu erklären.

Parameter

Auch wenn Blöcke keine Signatur wie wie eine Perl 6 sub haben, können Parameter sie übergeben werden. Nicht nur die weiter unten vorgestellten Blockmodifikatoren wie (given und for) geben einem Block "Input" in Form von Parametern, sondern die Referenz auf einen Block lässt sich ja auch in einem Skalar speichern. Wenn sie dann aufgerufen wird, können ihr auch Parameter wie einer sub gegeben werden. Man nennt das auch anonyme sub.

    my $a = { ... }                # anonymer Block
    $a(2);                         # ruft Block mit Parameter
    &$a(2);                        # Schreibweise von Perl 5 geht weiterhin
    &$a(2,3);                      # mehrere Parameter natürlich auch möglich

Kontextvariable

Wie aus Perl 5 bekannt, landet der Parameter an einen Block in Kontextvariable $_. Allerdings kennen einfache Blöcke in Perl 6, im Gegensatz zu einer sub, keine Variable namens @_ und $_ speichert natürlich immer nur einen Wert. Dies kann aber auch eine Arrayreferenz oder Hashreferenz sein.

    my $a = { .say }               # gibt $_ aus
    $a(2);                         # '2'
    $a(2,3);                       # auch '2'
    $a(<wald wiese>);              # 'waldwiese', da ein array 

automatisch benannte Parameter

Möchte man auf mehrere Parameter zugreifen, muß man einen Syntax benutzen, der vollständig neu ist.


pointy block

-> <->

Bedingte Ausführung

Operatoren

Der kürzeste Weg in Perl eine bedingte Anweisung zu schreiben, sind logische Auswahloperatoren. In den Beispielen, zu denen der Link führt, werden sie nur verwendet, um einen von 2 Werten zu liefern. Man kann sie aber auch zwischen 2 Ausdrücke oder Blöcke schreiben und damit die Ausführung des rechten Teils vom linken Teil abhängig machen. Bei or (niedrige Priorität) oder || (höhere Priorität) wird der rechte Teil ausgeführt, wenn der linke kein Ergebnis liefert ('' oder 0). Bei and oder && wird die Ausführung forgesetzt wenn die linke seite ein positives, nicht leeres (-1 ist in dem Fall positiv) Ergebnis liefert.

    $handle = open $datei or die 'krächz';      # neue Form von altbewährtem Perl 5 Code
    defined $lied && singe($lied);              # altbewährter perl5 Syntax geht auch in Perl6
    defined $lied ?? singe($lied) !! &schweig;  # ternärer Operator, ja, "&"-sigil gibts immer noch
    do {...} and do {!!!}                       # macht !!! wenn ... nichleeres Ergebnis liefert

Dies ist aber alles schon aus Perl 5 bekannt. Völlig neu sind hier nur andthen und orelse. andthen funktioniert wie ein and, nur daß der Inhalt der Kontextvariable ( $_), im Falle der Ausführung im rechten Block weiter besteht. Ebenso ähnelt orelse einem or, nur daß die Fehlermeldung in @! im rechten Block ausgewertet werden kann, wenn der linke Block oder Befehl einen negativen Rückgabewert abgeben.

if und unless

Auch auf die Gefahr gestandene Perler zu langweilen: Ist ein auf if folgender Ausdruck wahr, wird der darauf folgende Block einmal ausgeführt. Falls er nicht wahr ist und ihm ein optionales elsif folgt, von denen es beliebig viele geben kann, wird dessen Ausdruck geprüft. Trafen alle if und elsif Abfragen nicht zu, wird der Block nach dem else-Befehl ausgeführt (falls vorhanden). unless ist das Gegenstück zu if, das einen Block ausführen lässt, wenn der Ausdruck nicht wahr ist. Neu daran ist, daß man die Klammern um die Ausdrücke weglassen kann (dank strikterer Regeln Leerzeichen einfügen zu können), und einem unless darf nun auch elsif folgen.

    if $name eq 'ingy' { say 'YAML'x5 } else { say 'Spiffy klingt schick aber ...' }

    unless defined $name   { say 'Willkommen Fremder.'}
    elsif $name eq 'larry' { say 'Hey Mister Toady.' }
    else                   { say 'Noch so ein Perl Hacker.' }

given when

Perl hat endlich eine select Anweisung - hurra - nur die Schlüsselwörter dazu sind given (switch) und when (case), was eine natürliche, englische Formulierung nachahmen soll. Dabei überweist die nach given genannte Variable ihren Inhalt der Kontextvariable $_ und alle jeweils auf ein when folgende Ausdrücke werden per smart match ( ~~) mit $_ verglichen. Die erste when-Klausel, deren Ausdruck mit $_ matcht wird ausgeführt, danach wird der gesamte given block verlassen. Dieses Verhalten kann man dazu nutzen, zwischen die when Klauseln weitere Befehle einzufügen, die nur abgearbeitet werden, wenn bisherige Klauseln unzutreffend waren. Das macht den optionalen default-Block fast überflüssig, der ausgeführt wird, wenn keine der when-Klauseln zutraf. Trotzdem kann default (das einem when true entspricht) dabei helfen, deutlichen Code zu schreiben. Möchte man nach Ausführung einer Klausel auch die nächsten Klauseln prüfen lassen, füge man ein continue als letzten Befehl ein.

    given $alter {
        say "Willkommen bei der Beratungstell für Programmiersprachen."
        when  < 5         { say "Definitiv zu jung für Perl."  }
        when 14..18       { say "Genau richtig um anzufangen";  continue; }
        when 16|17|18     { say "Vielleicht grad mit Pubertät beschäftigt?"; continue;}
        say "ahhh ein älteres Semester hier ... interessant.";
        default           { say "Perl ist immer gute Wahl."    }
    }

Dieses Beispiel tut das selbe wie:

    given $alter {
        say "Willkommen bei der Beratungstell für Programmiersprachen."
        when $_ ~~ < 5         { say "Definitiv zu jung für Perl."  }
        when $_ ~~ 14..18      { say "Genau richtig um anzufangen";  continue; }
        when $_ ~~ 16|17|18    { say "Vielleicht grad mit Pubertät beschäftigt?"; continue;}
        say "ahhh ein älteres Semester hier ... interessant.";
        default                { say "Perl ist immer gute Wahl."    }
    }

given setzt also die Variable $_ die zu einem Alias wird, was man auch Topicalization nennt. Die kann man auch dazu verwenden einfachen Code zu kürzen also statt:

    $grossesobjekt.unterobjekt.hash{'schlüssel'} = $grossesobjekt.unterobjekt.hash{'schlüssel'}* 2
     - $grossesobjekt.unterobjekt.hash{'schlüssel'} / $grossesobjekt.unterobjekt.hash{'schlüssel'};

    # nun so:

    given $grossesobjekt.unterobjekt.hash{'schlüssel'} { $_ = $_ * 2 - $_ / $_ }

Da andere Befehle auch $_ setzen können, kann man when nun vielfach verwenden:

    for @array {
        when 'happy' {...}
        default      {...}
    }

Schleifen

Schleifen sind einfache Blöcke die mehrfach ausgeführt werden.

while und until

Wie bekannt wird eine while Schleife ausgeführt, solang der Ausdruck, der auf while folgt wahr ist. Ähnlich unless ist until das Gegenstück zu while, daß einen Schleifenblock ausführen lässt, bis die geforderte Bedingung erfüllt ist. Dabei wird (wie auch bei while) vor jeder Wiederholung der Asdruck (der ebenfalls nicht mehr von Klammern umgeben sein muss) vollständig neu ausgewertet.

    while $alter < 5 { $alter++ }          # wart noch ein Jährchen
    until $alter > 4 { $alter++ }          # macht das gleiche

repeat

Da do jetzt nur noch zur einmaligen Ausführung anweist, brauch es jetzt einen neuen Befehl für "do {...} while" Konstrukte, die bisher eh noch nicht vollwertige Schleifen waren. Da Perl sich hauptsächlich an der englischen Sprache orientiert wird dieser Befehl jetzt repeat genannt. (repeat so and so until so and so)

    repeat {$i++} while $i < 5;             # führt Block mindestens einmal aus
    repeat {$i--} until $i > 1;             # dito

loop

Der loop Befehl ist der neue allgemeine Schleifenbefehl der 2 Aufgaben erfüllt. 1) Ohne weitere Angaben ist es eine Endlosschleife. 2) Mit runden Klammern und 2 Semikolon wird es zur Schleife im alten C-Stil, wofür man vorher for oder foreach verwendete.

    loop                   { sing "la" }    # Endlosschleiffe, while 1 {...} geht aber auch noch
    loop ($i=0; $i<5; $i++){ 'zabduabdab' } # 5 Wiederholungen im alten C-Stil

for

Schleifen mit for iterieren nun ausschliesslich über Arrays, wie sie es bereits vorher konnten, nur die zweite Schreibweise ( foreach ) fällt weg. Wie gewohnt ist in der Kontextvariable $_ immer der Wert des aktuellen Arrayelementes, es sei, man benennt explizit eine andere Iteratorvariable, aber man kann natürlich auch die automatisch benannten Blockparameter ($^a .. $^z) benutzen. Zum benennen der lokalen Schleifenvariablen benutzt man jetzt am besten die pointy sub genannte Schreibweise ('->').

    for @zahlen                  { say }         # gibt alle Arrayelemente aus, nutzt $_
    for @zahlen                  { say $_ }      # das Selbe, "$_.say" oder ".say" ginge auch
    for @zahlen  -> $zahl        { say $zahl }   # expliziterer Weg für gleiches Ergebnis
    for @zahlen, sub ($zahl)     { ... }         # tut das gleiche, siehe Kapitel 6:pointy sub
    for @zahlen  -> $z1, $z2     { say "$z1:$z2" }      # nimmt jedesmal 2 Elemente aus Array
    for @a Z @b  -> $a, $b       { say "[$a, $b]" }     # 2 Arrays parallel ausgeben
    for %hash.kv -> $key, $value { say "$key => $value"}# Wertepaare eines Hash ausgeben
    for =<>                      { ... }         # neu für while (<>) {...}
    for =$*IN -> $line           { ... }         # andere Schreibweise, Listen sind lazy
    for 0..Inf -> $zahl          { ... }         # funzt auch wegen lazy Listen

Weitere Iteratoren

Sprungbefehle

Innerhalb von Schleifenblöcken gelten besondere Sprungbefehle. redo bewirkt das nochmalige Ausführen des aktuellen Schleifendurchlaufs, next lässt zum nächsten Durchlauf springen und last bewirkt ein sofortiges verlassen der Schleife.


Nachgestellte Schreibweisen

Steht im Block nur ein Befehl, kann man bei if, unless, when, while, until und for die kürzere, nachgestellte Schreibweise wählen.

    krabbel() if $alter < 3;
    $alter++ while $alter < 5;
    .say for @zahlen;                       # allerkürzeste Variante


Übersicht || 0:Vor | 1:Ge | 2:Theo | 3:Var | 4:Op | 5: Ein- und Ausgabe | 6: Ablaufstrukturen | 7: Subroutinen | 8:OOP | 9:Rx || A:Index | B:Tab | C:Tour | D:Delta | E:Links

-- HerbertBreunung - 03 Apr 2006

-- TinaMueller - 18 Apr 2006 - kleine korrekturen

-- MoritzLenz - 26 Feb 2007 - for @foo {say} durch {.say} ersetzt
Topic revision: r54 - 2009-12-03 - 00:41:45 - HerbertBreunung
 
Bitte die NutzungsBedingungen beachten.
Bei Vorschlägen, Anfragen oder Problemen mit dem PerlCommunityWiki bitten wir um WebBottomBarExample">Rückmeldung.