Programmablauf

Innerhalb des Hauptprogramms werden die Programmzeilen Schritt für Schritt abgearbeitet. Das geschieht rein hierarchisch.

Der folgende Code blendet bei Tanken Einfahrt ein Symbol ein, wartet 1 sek. und gibt dann den Text Sensoren Tanken Einfahrt überfahren als der Sprachansage aus.

...
begin
  
  cpSymbolEvent('Überfahrt TankenEinfahrt');
  cpSleep(1000);
  cpSpeech('Sensoren TankenEinfahrt überfahren');
  
end.

Hier läuft es genau anders herum. Zuerst Sprachausgabe, dann warten, dann Symbol einblenden.

...
begin
  
  cpSpeech('Sensoren TankenEinfahrt überfahren');
  cpSleep(1000);
  cpSymbolEvent('Überfahrt TankenEinfahrt');
  
end.

Schleifen

Nicht alle Aufgaben lassen sich durch derart strikte Abläufe lösen. Manchmal muss ein bestimmter Code-Teil wiederholt werden, bis ein bestimmtes Ereignis eintritt.

Pascal Script bietet für diese Szenarien drei Optionen, von denen jede andere Lösungsansätze umfasst.

While

Die Befehlszeile lautet komplett while Bedingung do. Der darauf folgende Befehl wird so lange wiederholt, bis die Bedingung zutrifft. Sollen mehrere Befehle nacheinander wiederholt werden, müssen diese in einen begin … end; Block eingeschlossen werden. Dieser Block weist den Compiler an, alle Befehle als zur while-Bedingung zugehörig zu betrachten.

var
  runde : Integer;
  i : Integer;

begin

  runde := 10;
  i := 0;
  
  while i < 5 do
  begin
    runde := runde + 1;
    Inc(i);
  end;

  cpShowMessage('Wert von Runde: ' + IntToStr(runde));
  
end.

Die Variable Runde hat am Ende den Wert 15. Warum ist das so? Der Durchlauf der Schleife beginnt bei Wert i = 0 und geht bis i = 4. Das sind 5 Durchläufe und entsprechend werden +5 zum Ausgangswert der Variablen runde addiert.

Wichtig ist bei der While-Schleife immer, die Bedingung für den Ausstieg im Auge zu behalten. Tritt diese nicht ein, läuft der Code unendlich weiter. Der einzige Ausweg führt dann über den harten Ausstieg durch das Abschießen der gesamten Rennbahnzeitmessung über den Taskmanager von Windows.

Repeat

Prinzipiell funktioniert die Repeat-Schleife genauso wie die While-Schleife. Wo ist dann der Unterschied? Schauen wir uns dazu direkt das Beispiel an:

var
  runde : Integer;
  i : Integer;

begin

  runde := 10;
  i := 0;
  
  repeat
    runde := runde + 1;
    Inc(i);
  until i < 5;

  cpShowMessage('Wert von Runde: ' + IntToStr(runde));
  
end.

Auf den ersten Blick sieht doch alles ähnlich aus, oder? Trotzdem fehlen beim Wert für die Variable Runde 4 Durchläufe. Damit sind wir beim Thema.

Bei der While-Schleife steht die zu prüfende Bedingung vor dem zu durchlaufenden Code. Erst wenn, bzw. solange die Bedingung zutrifft, wird die Schleife immer wieder durchlaufen.

In der Repeat-Schleife steht die Bedingung am Ende. Sie wird also erst geprüft, nachdem die Codezeilen einmal durchlaufen wurden. Das passiert hier im Beispiel genau 1x. Dann stellt die Bedingungsprüfung fest, dass diese erfüllt ist. Der Wert von i ist kleiner als 5!

Deshalb gibt es zwei wichtige Dinge zu beachten:

  • Die zu prüfende Bedingung muss anders formuliert sein.
  • Der Code innerhalb der Repeat-Schleife wird, im Gegensatz zur While-Schleife, mindestens 1x durchlaufen.

For

Die While- und die Repeat-Schleife sind mehr für eine unbestimmte Anzahl von Schleifendurchläufen ausgelegt. Natürlich lässt sich auch dort die genaue Anzahl festlegen. Das ist z.B. in dem gewählten Beispiel so. Allerdings wäre denkbar, dass die Variable i durch Vorgänge im Code vorzeitig einen Wert annimmt, der die Schleife zum Abbruch bringt, weil die gerpüfte Bedingung erfüllt ist.

Dem gegenüber gibt es die For-Schleife, die für die Ausführung mit einer festgelegten Anzahl von Durchläufen gedacht ist.

var
  runde : Integer;
  i : Integer;

begin

  runde := 10;
  
  for i := 0 to 5 do
  begin
    runde := runde + 1;
  end;
  
  cpShowMessage('Wert von Runde: ' + IntToStr(runde));
  
end.

Der Wert ist letztlich '16', weil der Schleifendurchlauf mit dem Wert '0' für die Variable 'i' beginnt. Damit gibt es insgesamt 6 Durchläufe.

Es geht bei der For-Schleife auch anders herum. Man beginnt man bei einem höheren Wert und zählt herunter. Das zeigt das nächste Beispiel.

var
  runde : Integer;
  i : Integer;

begin

  runde := 10;
  
  for i := 10 downto 1 do
  begin
    runde := runde - 1;
  end;
  
  cpShowMessage('Wert von Runde: ' + IntToStr(runde));
  
end.

Der Wert ist der Variablen Runde ist '0', da die Anzahl der Durchläufe mit 10 exakt dem Startwert der Variablen entspricht.

Fallunterscheidungen

In vielen Fällen reagiert ein Programm auf bestimmte Werte, die auf festen Vorgaben, Berechnungsergebnissen oder anderen Ereignissen beruhen. Hier muss von Fall zu Fall unterschieden und der passende Code ausgeführt werden.

Dazu bietet Pascal Script zwei Wege an.

If ... then ... else

Dieses Konstrukt dürfte wohl die bekannteste Form sein, innerhalb eines Programms auf unterschiedlichste Bedigungen zu reagieren.

In Kurzform Führe Befehlsblock a aus, wenn die Bedingung zutrifft oder, falls das nicht der Fall ist, führe Befehlsblock b aus.

Im folgenden Beispiel wird geprüft, ob der Listenplatz im StartCenter (slot) und die Regler-ID übereinstimmen. Abhängig vom Ergebnis des Vergleichs wird eine Meldung ausgegeben.

var
  slot : Integer;
  id: Integer;
  
begin

  slot := Cockpit.Slot;
  Cockpit.Slot := slot;
  id := Cockpit.SlotID;
  
  if id = slot then
    // Übereinstimmung
    cpShowMessage('Regler-ID und Slot-Index sind identisch')
  else
    // Keine Übereinstimmung
    cpShowMessage('Regler-ID und Slot-Index sind unterschiedlich');
  
end.


If-Abfragen lassen sich beliebig erweitern. Das ist jedoch bei zunehmender Anzahl der Bedingungen nachteilig für die Performances des Codes.

var
  slot : Integer;
  position : Integer;
  sFahrer : String;

begin

  slot := Cockpit.Slot;
  Cockpit.Slot := slot;
  sFahrer := Cockpit.FahrerName;
  position := Cockpit.Position;

  // Mehrere if-Bedingungen (else-if) 
  if (sFahrer <> 'Ralph525') AND (position > 2) then
    // Meldung ausgeben
    cpShowMessage('Kopf hoch. Du schaffst noch P2.')
  else if (sFahrer = 'slot-xtreme') OR (sFahrer = 'Fieser-Kardinal') then
    cpShowMessage('Prima. Du wirst sicher Erster.')
  else if (sFahrer = 'Ralph525') AND (position = 3) then
    cpShowMessage('So soll das sein.')
  else
    // Meldung, falls keie andere Bedingung zutrifft
    cpShowMessage('Egal. Hauptsache Spass.');
    
end.

Wichtig: Vor sämtlichen else if bzw. am Ende der Meldungsbefehlszeile steht kein Semikolon als Befehlsabschluss. Der Befehl gilt erst mit dem letzten else abgeschlossen. Hat man Befehlsblöcke pro Bedingung und arbeitet mit begin … end, sieht das so aus:

...
  if sFahrer = 'slot-xtreme' then
  begin
  
    // sFahrer hat gewonnen
    if position = 1 then
    begin
      // Meldung und Symbol anzeigen, Sound abspielen
      cpShowMessage('Spitze. Du hast gewonnen.');
      cpSound('Applaus.mp3');
      cpAddOnSymbolEvent('Pokal', 1, true);
    end
    else
      cpShowMessage('Schade. Nächstes mal klappt's sicher.');
  end;
...

Nach dem end ist kein Befehlsabschluss in Form des Semikolons vorhanden, nach den Zeilen innerhalb des Blocks schon!

Ein weiterer Aspekt ist die verschachtelte Schreibweise beider if-Abfragen. Tatsächlich ließe sich dieser Block auch so schreiben:

...
  if (sFahrer = 'slot-xtreme') AND (position = 1) then
  begin
    ...
  end
  else
    ...;

Teilweise ist es Geschmacksache, ob man die eine oder andere Notation verwendet, teils wird es in verschachtelter Form einfacher, den Code nachzuvollziehen. Ich bevorzuge die verschachtelte Variante.


Case ... of

Prinzipiell funktioniert dieser Befehl ähnlich wie if … then … else. Ich persönlich finde ihn allerdings schöner, sowohl was die Lesbarkeit im Code betrifft, als auch in seiner Form beim programmieren.

Bei längeren Fallunterscheidungen ist er darüberhinaus performanter als die if-Verschachtelungen. Im Internet ist die Rede von 5 Fallunterscheidungen und mehr. Er ist jedoch nicht langsamer als if … then … else, wenn es um weniger Varianten geht.

Codebeispiele


{StartZiel Event}
{Pascal Script Engine}

var
  slot : Integer;
  id : Integer;
  position : Integer;
  sFahrer : String;
  
begin

  slot := Cockpit.Slot;
  Cockpit.Slot := slot;
  id := Cockpit.SlotID;
  position := Cockpit.Position;
  sFahrer := Cockpit.FahrerName;
  
  case position of
  
    1,2: cpShowMessage('Fahrer: ' + sFahrer + ' - ID ' + IntToStr(id) );
    
    3  : 
        begin
          if sFahrer <> 'Ralph525' then
            cpShowMessage('Nicht zulässiges Ergebnis:')
          else
            cpShowMessage('Du bist immerhin auf dem Podium, ' + sFahrer);
         end;
         
    4..6: cpShowMessage('Wer bekommt die rote Laterne?');
    
  else
    cpShowMessage('Es gibt nur 6 Rennteilnehmer');
    
  end;
  
end.

Dieses Beispiel zeigt bei Überfahrt von Start/Ziel eine Meldung abhängig Platzierung eines von 6 Fahrern als Meldung an. Wird Platz 7 oder 8 erkannt, führt das zur Anzeige einer Meldung, dass nur 6 Teilnehmer zugelassen sind.

Bei den Plätzen 1 und 2 zeigt die Meldung den Namen des Piloten und die zugehörige Regler-ID.

Position 3 bekommt einen Codeblock, der auf den Fahrernamen reagiert.

Die Positionen 4 bis 6 werden mit der gleichen Meldung motiviert, sich ein wenig ins Zeug zu legen, um die Ankunft auf dem letzten Platz abzuwenden.

Es geht jedoch weniger um die Inhalte, als vielmehr um die Möglichkeiten. Mehrere nicht zusammenhängende Werte lassen sich mit Trennung per Komma einem Fall zuordnen (1,2,6). Zu beachten ist nur, dass die abgefragen Werte dem Variablen-Typ entsprechen. Einer Abfrage von Integer-Variablen lassen sich keine Zeichenfolgen „unterschieben“. Ausnahme ein String „26“, der sich mit der Funktion StrToInt() in einen Integerwert umwanddeln lässt (StrToInt('26') = 26).

Gibt es einen zusammenhängenden Wertebereich wird dieser mit zwei Punkten zwischen Anfangs- und Endwert beschrieben. Das findet sich bei den Arrays wieder. Grob gesagt wird hier ein Werte-Array übergeben (1..100), etc.

Auch einzelne Werte sind möglich und ebenfalls lässt sich eine Regel else für alle Werte festlegen, die nicht gesondert aufgeführt sind.

Wie alle Befehle muss case … of mit einem end; abgeschlossen werden.

Gut zu erkennen ist auch, dass für den Fall position = 3 genauso gut ein Befehlsblock für die weitere Verarbeitung möglich ist.


Zurück zum letzten Beispiel aus dem if … then … else Teil.

var
  slot : Integer;
  position : Integer;
  sFahrer : String;
  
begin
  slot := Cockpit.Slot;
  Cockpit.Slot := slot;
  position := Cockpit.Position;
  sFahrer := Cockpit.FahrerName;
  
  // Meldungen für einen bestimmten Fahrer
  if sFahrer = 'slot-xtreme' then
  begin
  
    // Meldung abhängig von der Platzierung
    case position of
      1: cpShowMessage('Spitze, ' + sFahrer + ' Du führst.');
      2: cpShowMessage(sFahrer + ', Platz 2. Du kannst noch gewinnen.');
      3: cpShowMessage('Dieser Platz ist für Ralph525 reserviert.');
      4: cpShowMessage('Reicht es heute nicht fürs Podium, ' + sFahrer + '?');
      5: cpShowMessage('So schlecht warst Du lange nicht mehr, ' + sFahrer);
      6: cpShowMessage(sFahrer + ': Unglaublich. Erbärmlich. Ohne Worte.');
    end;
    
  end;
  
end.

Dieses Beispiel mit if … then … else sähe so aus:

var
  slot : Integer;
  position : Integer;
  sFahrer : String;
  
begin
  slot := Cockpit.Slot;
  Cockpit.Slot := slot;
  position := Cockpit.Position;
  sFahrer := Cockpit.FahrerName;
  
  // Meldungen für einen bestimmten Fahrer
  if sFahrer = 'slot-xtreme' then
  begin
  
    // Meldung abhängig von der Platzierung
    if position = 1 then
      cpShowMessage('Spitze, ' + sFahrer + ' Du führst.')
    else if position = 2 then
      cpShowMessage(sFahrer + ', Platz 2. Du kannst noch gewinnen.')
    else if position = 3 then
      cpShowMessage('Dieser Platz ist für Ralph525 reserviert.')
    else if position = 4 then
      cpShowMessage('Reicht es heute nicht fürs Podium, ' + sFahrer + '?')
    else if position = 5 then
      cpShowMessage('So schlecht warst Du lange nicht mehr, ' + sFahrer)
    else if position = 6 then
      cpShowMessage(sFahrer + ': Unglaublich. Erbärmlich. Ohne Worte.');
    
  end;
  
end.

Die Entscheidung, welche Version für euch die „schönere“ und einfachere ist, liegt bei jedem persönlich. Für mich ist es case … of, weil ich nicht überlegen muss, ob ein Semikolon ans Ende der Zeile gehört oder nicht. Zudem finde ich die case … of Zeilen besser lesbar und würde ihnen jederzeit den Vorzug vor if … then … else geben.



Start - FAQ - Glossar - Sitemap - Impressum

 
cockpit-xp/addon/programmablauf.txt · Zuletzt geändert: 2020/11/28 12:14 (Externe Bearbeitung)