.. highlightlang:: us .. _kontroll-anweisungen: Kontroll-Anweisungen ==================== Kontrollanweisungen bestimmen die Reihenfolge, in der Aktionen durchgeführt werden. Vorbild für die Kontrollstrukturen von UniScript war die Programmiersprache C; die :keyword:`for`-Anweisung von UniScript weicht jedoch von der C-Version ab. Die :keyword:`try`-:keyword:`except`-Anweisung ist nur bei wenigen C-Compilern vorhanden; eine Variante existiert jedoch bei den moderneren C++-Compilern. Die ``switch``-Anweisung und die ``do``-``while``-Anweisung fehlen in UniScript. Diese Anweisungen sind jedoch einfach durch :keyword:`if`-:keyword:`else`-:keyword:`if`-Ketten bzw. die :keyword:`while`-Anweisung zu ersetzen. Die ``goto``-Anweisung fehlt ebenfalls, weil es für sie nur sehr wenige vernünftige Verwendungen gibt. .. index:: if .. index:: else .. _if: .. _else: .. _die-if-else-anweisung: Die if-else-Anweisung --------------------- Die :keyword:`if`-:keyword:`else`-Anweisung wird für Entscheidungen verwendet. Sie hat die Form :: if (bedingung) { if-anweisungen } else { else-anweisungen } Der Ausdruck ``bedingung`` muss reell sein. Die **if**-Anweisungen werden ausgeführt, wenn die Bedingung wahr ist (d.h. wenn der skalare Ausdruck ungleich 0 ist) und die :keyword:`else`-Anweisungen werden ausgeführt, wenn die Bedingung falsch, d.h. 0 ist. Der **else**-Teil kann entfallen :: if (bedingung) { if-anweisungen } Gehört zu den :keyword:`if`- und :keyword:`else`-Teilen nur jeweils eine Anweisung, kann auf die geschweiften Klammern verzichtet werden :: a = 1; if (a == 1) print "a ist 1" else print "a ist nicht 1" Übersichtlicher ist es jedoch, wenn die Anweisungen geklammert werden und eine Anweisung pro Zeile geschrieben wird. :: a = 1 if (a == 1) { print "a ist 1" } else { print "a ist nicht 1" } Die folgende Schreibweise ist in UniScript **nicht erlaubt**, weil UniScript die zweite geschweifte Klammer als Ende der :keyword:`if`-:keyword:`else`-Anweisung interpretiert und beim Wort :keyword:`else` einen Syntaxfehler produziert. :: a = 1; if (a == 1) { print "a ist 1"; } else { print "a ist nicht 1"; } Wer die Anweisungen unbedingt in dieser Form schreiben will, kann sich mit :ref:`continuation-line` behelfen: :: a = 1; if (a == 1) { print "a ist 1"; } .. else .. { print "a ist nicht 1"; } Die Bedingung muss, wie bereits bemerkt, ein reeller Skalar sein. Der Ausdruck ``string == "Hallo"`` liefert dann einen reellen Skalar, wenn ``string`` ein Skalar ist, nämlich 1, wenn in ``string`` ``"Hallo"`` steht und sonst 0. Falls ``string`` jedoch einen Vektor oder eine Matrix von Strings enthält, liefert der Vergleichsausdruck einen reellen Vektor oder eine reelle Matrix. Enthält ``string`` z. B. den aus drei Elementen bestehenden Vektor ``["Hallo", "HALLO", "hallo"]`` liefert der Vergleichsausdruck ``string == "Hallo"`` den reellen Vektor ``[1, 0, 0]``. Um diesen reellen Vektor in einen reellen Skalar umzuwandeln, existieren die beiden UniScript Funktionen :ref:`all` und :ref:`any`. Falls :ref:`all` mit einem reellen Vektor aufgerufen wird, liefert :ref:`all` den Skalar 1, wenn **alle** Elemente den Wert 1 haben und 0, wenn das nicht der Fall ist. Entsprechend liefert :ref:`any` den Wert 1, wenn irgend **ein** Wert des Eingabevektors den Wert 1 hat. Falls das Argument von :ref:`all` oder :ref:`any` eine reelle Matrix ist, wird die Matrix spaltenweise ausgewertet. Die Funktionen liefern dann einen Zeilen-Vektor. Durch doppeltes Anwenden der Funktionen kann daraus ein Skalar gemacht werden. Das folgende Beispiel führt also ``Aktion-1`` nur aus, wenn alle Elemente von ``string`` den Wert ``"Hallo"`` haben, unabhängig davon, ob ``string`` ein Skalar, ein Vektor oder eine Matrix ist. :: if (all(all(string == "Hallo"))) { // Aktion-1 } else { // Aktion-2 } Häufig werden :keyword:`if`-:keyword:`else`-Anweisungen verwendet, um eine Entscheidung aus einer Reihe von Alternativen zu treffen. In diesem Fall werden sogenannte :keyword:`else`-:keyword:`if`-Ketten verwendet: :: if (bedingung-1) { anweisung-1 } else if (bedingung-2) { anweisung-2 } else if (bedingung-3) { anweisung-3 } else if (bedingung-4) { anweisung-4 } else { anweisung-n } Die Bedingungen werden nacheinander geprüft, bis eine Bedingung zutrifft. Die entsprechenden Anweisungen werden ausgeführt und die :keyword:`else`-:keyword:`if`-Kette wird beendet. Falls keine Bedingung zutrifft, wird der letzte :keyword:`else`-Fall ausgeführt. .. index:: for .. index:: in .. _for: .. _in: .. _die-for-anweisung: Die for-Anweisung ----------------- Die :keyword:`for`-Anweisung wird dazu verwendet, über alle Elemente eines Vektors zu iterieren. :: for (i in vektor) { for-anweisungen } Hat ``vektor`` den Wert ``[1,3,6,4]`` nimmt ``i`` nacheinander die Werte 1, 3, 6, 4 an. ``vektor`` kann auch ein String-Vektor oder ein komplexer Vektor sein. Der Vektor wird nur einmal am Anfang der :keyword:`for`-Schleife ausgewertet. Das folgende Beispiel :: a = [1,2,3] for (i in a) { a = 0; printf("a = %d, i = %d\n", a, i); } erzeugt die Ausgabe :: a = 0, i = 1 a = 0, i = 2 a = 0, i = 3 Das folgende Beispiel verwendet die :ref:`DocCreate`-Funktion um drei UniPlot-Dateien zu öffnen: :: svFilename = ["be_ia.ipw", "fit.ipw", "no_x.ipw"]; svFilename = GetRootDirectory() + "examples/" + .. svFilename; for (ssName in svFilename) { DocCreate(ssName); } .. index:: while .. _while: .. _die-while-anweisung: Die while-Anweisung ------------------- Die :keyword:`while`-Anweisung wird verwendet, um eine Anweisung oder eine Folge von Anweisungen mehrfach auszuführen. Man bezeichnet dies als Schleife. :: while (bedingung) { while-anweisungen } ``bedingung`` muss wie bei der :keyword:`if`-Anweisung ein reeller skalarer Ausdruck sein. Die Anweisungen werden wiederholt ausgeführt solange die Bedingung *wahr*, also ungleich 0 ist. :: while (1) { print "Hallo" } würde also ununterbrochen ausgeführt, da die Bedingung nie den Wert 0 annimmt. Sie kann nur von außen abgebrochen werden, indem man die ESCAPE-Taste (ESC-Taste) betätigt. Man sieht solche ``while(1)``-Schleifen dennoch häufiger. Sie werden dann jedoch durch eine :keyword:`break`- oder :keyword:`return`-Anweisung abgebrochen. Diese :keyword:`while`-Schleife gibt genau 10 mal das Wort ``"Hallo"`` aus und druckt am Ende die Zahl 11. :: i = 1; while (i <= 10) { print "Hallo"; i = i + 1; } print i; .. index:: break .. index:: continue .. _break: .. _continue: .. _die-break--und-die-continue-anweisung: Die break- und die continue-Anweisung ------------------------------------- Die :keyword:`break`-Anweisung wird in Verbindung mit der :keyword:`if`-Anweisung dazu verwendet, eine :keyword:`while`- oder :keyword:`for`-Anweisung vorzeitig abzubrechen. Einige Sprachen haben eine Schleife, die am Ende prüft, ob die Anweisungen der Schleife nochmal ausgeführt werden sollen (bei Pascal ist dies die ``repeat``-Schleife, bei C die ``do``-Schleife). Mit der :keyword:`break`-Anweisung kann dieses Verhalten in UniScript simuliert werden. Das folgende Beispiel druckt 10 mal ``"Hallo"`` und am Ende die Zahl 10. :: i = 1; while (1) { print "Hallo"; if (i == 10) break; i = i + 1; } print i; Mit der :keyword:`continue`-Anweisung können Anweisungen innerhalb einer :keyword:`for`- oder :keyword:`while`-Schleife übersprungen werden. Hier ein Beispiel, das alle Dateien aus einem Verzeichnis öffnet, die die Endung :file:`.ipw` haben. :: ssPath = "c:/uniplot/samples/"; svFiles = FindFiles(ssPath + "*.*"); for (ssName in ssPath + svFiles[;1]) { if (strupper(SplitPath(ssName)[4]) != ".IPW") { continue; } if (DocCreate(ssName) == 0) break; } ``FindFiles(ssPath + "*.*")`` liefert eine Stringmatrix mit den Dateien des Verzeichnisses ``ssPath``. In der ersten Spalte der Matrix befinden sich die Dateinamen. In der zweiten Spalte befindet sich die Größe der Dateien und in der dritten Spalte die Dateiattribute. Diese beiden Angaben interessieren hier nicht, weshalb sie mit dem Ausdruck ``svFiles[;1]`` herausgefiltert werden. Die Funktion :ref:`SplitPath` teilt einen vollständigen Dateinamen in seine Bestandteile Laufwerk, Pfad, Name und Erweiterung auf. Der Aufruf ``SplitPath(ssName)[4]`` liefert dabei die Dateinamenserweiterung. :ref:`strupper` wandelt sein Argument in Großbuchstaben um, so daß die Bedingung auch zutrifft, wenn :ref:`SplitPath` eine Dateinamenserweiterung in Kleinbuchstaben liefert. Wenn die Bedingung nicht zutrifft, wird die :keyword:`continue`-Anweisung ausgeführt. Dies bewirkt, daß ganz ans Ende, also unmittelbar **vor** die schließende geschweifte Klammer, gesprungen wird. Falls die Funktion :ref:`DocCreate` die Zahl 0 zurückliefert, konnte das Dokument nicht erzeugt werden. Es wird dann die :keyword:`break`-Anweisung ausgeführt, wodurch unmittelbar **hinter** die :keyword:`for`-Schleife gesprungen wird. Man hätte bei dem Beispiel übrigens auf die :keyword:`continue`-Anweisung verzichten können, indem man in :ref:`FindFiles` als Suchmuster ``"*.ipw"`` angegeben hätte, also ``FindFiles(ssPath + "*.ipw")``. .. index:: try .. index:: except .. _try: .. _except: .. _die-try-except-anweisung: Die try-except-Anweisung ------------------------ Die :keyword:`try`-:keyword:`except`-Anweisung dient hauptsächlich dazu, Programmierfehler abzufangen und die Fehlerbehandlung zu vereinfachen. Sie hat die Form :: try { try-anweisungen } except (bedingung) { except-anweisungen } Im Gegensatz zur :keyword:`if`-:keyword:`else`-Anweisung ist der zweite Block nicht optional. Zu jedem :keyword:`try`-Block gehört genau ein :keyword:`except`-Block. :keyword:`try`-:keyword:`except`-Blöcke können geschachtelt werden. Normalerweise werden nur die :keyword:`try`-Anweisungen ausgeführt, d. h. im Normalfall wird weder ``bedingung`` ausgewertet, noch die :keyword:`except`-Anweisungen ausgeführt. Es gibt jedoch eine Reihe von Gründen, die dazu führen, daß während der Ausführung der :keyword:`try`-Anweisungen ein Problem auftritt. **Beispiele**: * Es ist nicht genug Speicher vorhanden, um eine der Anweisungen auszuführen. * Der Benutzer hat die ESC-Taste gedrückt, während eine der :keyword:`try`-Anweisungen ausgeführt wurde. * Es wurde auf ein Element eines Vektors zugegriffen, das nicht existiert. * Falls die :keyword:`try`-Anweisungen Funktionsaufrufe enthalten, kann ein Fehler innerhalb der Funktion aufgetreten sein. * Innerhalb einer Funktion kann die :ref:`error`-Funktion aufgerufen worden sein. Der 1. und der 2. Fall sind Probleme, die vom Programmierer nicht vorhersehbar sind. Der 3. und 4. Fall sind im allgemeinen Programmierfehler. Man bezeichnet die Fälle als *Ausnahmen* und die Lösung der Probleme als Ausnahme-Behandlung (exception handling). Im 5. Fall (Aufruf der :ref:`error`-Funktion) wird bewußt eine Ausnahme erzeugt. **Beispiel**: :: def GetTextFile(ssFileName) { fp = fopen(ssFileName, "rt"); ssText = fread(fp, "char"); fclose(fp); return ssText; } :ref:`fopen` öffnet eine Datei zum Lesen, :ref:`fread` liest aus der Datei (bei dieser Form des Aufrufs die gesamte Datei) und :ref:`fclose` schließt die Datei. Es ist wichtig, daß eine Datei nach ihrer Verwendung wieder geschlossen wird, da (je nach Betriebssystem) nur eine kleine Anzahl Dateien gleichzeitig geöffnet werden dürfen. Ruft man die Funktion mit ``GetTextFile("d:\\test.txt")`` auf, dann gibt die Funktion den Inhalt der Datei ``"d:\\test.txt"`` als Zeichenkette an den Aufrufer zurück. Voraussetzung ist, daß die Datei existiert, genug Speicher vorhanden ist, um die Datei zu lesen, in den Funktionen (z. B. :ref:`fopen`) kein Fehler ist und der Benutzer nicht während der Funktionsaufrufe die ESC-Taste gedrückt hat. Falls eine dieser Ausnahmen auftritt, wird das Programm mit einer Fehlermeldung abgebrochen. Das Problem ist, daß dieser Fehler auftreten kann, bevor die :ref:`fclose`-Funktion aufgerufen wird. Es gibt einen sogenannten Ressourcen-Verlust. Das Problem kann gelöst werden, indem man die problematischen Anweisungen in einem :keyword:`try`-:keyword:`except`-Block schützt. :: def GetTextFile(ssFileName) { fp = 0; try { fp = fopen(ssFileName, "rt"); ssText = fread(fp, "char"); if (isreal(ssText)) error(); fclose(fp); return ssText; } except (1) { if (fp != 0) fclose(fp); MessageBox("Datei kann nicht geladen werden!"); return ""; } } Falls irgend eine Ausnahme innerhalb des :keyword:`try`-Blocks auftritt, wird in den :keyword:`except`-Block verzweigt, in dem die Ausnahme behandelt werden kann. In diesem Beispiel wird die Datei geschlossen (falls sie von :ref:`fopen` erfolgreich geöffnet wurde) und eine Fehlermeldung in einem Meldungsfenster ausgegeben. Hinter dem Schlüsselwort :keyword:`except` steht eine in Klammern eingeschlossene Bedingung. Ist die Bedingung 0, wird die Ausnahme nicht im folgenden :keyword:`except`-Block behandelt. Ist die Bedingung ungleich 0 (im Beispiel wurde die Konstante 1 verwendet), wird der :keyword:`except`-Block beim Auftreten einer Ausnahme ausgeführt. Innerhalb der Bedingung kann mit der Funktion :ref:`GetExceptionCode` festgestellt werden, welche Ausnahme aufgetreten ist. **Beispiel**: :: def TestException(a, b) { try { for (i in 1:10000) { x = a + b; } } except (GetExceptionCode() == ICERR_INTERRUPT) { MessageBox("Benutzer-Abbruch"); } } Wird die Funktion z. B. mit den Argumenten ``"a"`` und ``1.6`` aufgerufen, ``TestException("a", 1.6)``, tritt eine Ausnahme auf. Dadurch wird der Ausdruck :: GetExceptionCode() == ICERR_INTERRUPT ausgewertet. :ref:`GetExceptionCode` liefert eine Fehlernummer - in diesem Fall die Fehlernummer mit dem Namen ``ICERR_OPERATOR_TYPE``. Da die Fehlernummer nicht ``ICERR_INTERRUPT`` ist, wird der :keyword:`except`-Block nicht ausgeführt; die Ausnahme wird in diesem Fall nicht in der Funktion behandelt. Die Ausnahme wird nur behandelt, wenn der Benutzer in der :keyword:`for`-Schleife die ESC-Taste betätigt und dadurch die Ausnahme ``ICERR_INTERRUPT`` auslöst. Die Definitionen für die UniScript-Fehlernummern befinden sich in der Datei :file:`alias.ic`. :sub:`id-1192040`