Kontrollstrukturen: Unterschied zwischen den Versionen

Aus EINI
Wechseln zu: Navigation, Suche
(Wiederholung mit Schleifen)
 
(9 dazwischenliegende Versionen von 2 Benutzern werden nicht angezeigt)
Zeile 1: Zeile 1:
Um die formalen Eigenschaften eines Algorithmus in die syntaktischen Eigenschaften eines Programmes zu überführen, ist es von nöten einzelne Anweisungen eines Algorithmus' miteinander zu verknüpfen.
+
Um die formalen Eigenschaften eines [[Algorithmus]] in die [[Syntax und Semantik|syntaktischen]] Eigenschaften eines [[Programm|Programms]] zu überführen, ist es notwendig, die einzelnen Anweisungen eines Algorithmus miteinander zu verknüpfen.
  
In vielen Programmiersprachen, so auch im speziellen in Java, gibt es dafür eine kleine Grundmenge an sogenannten Kontrollstrukturen, die den Fluss eines Programmes steuern.
+
In vielen [[Programmiersprache|Programmiersprachen]], so auch in [[Java]], gibt es dafür eine kleine Grundmenge an sogenannten '''Kontrollstrukturen''', die den Fluss eines Programms steuern. Diese sind im speziellen  
 
+
*die sequenzielle Ausführung einzelner Programmanweisungen,
Diese sind im speziellen: Die sequenzielle Ausführung einzelner Programmanweisungen, das Zusammenfassen von Anweisungssequenzen in Blöcken, das Formulieren von alternativen Programmpfaden, das Formulieren von Fallunterscheidungen und das wiederholte Durchführen von Programmsequenzen.
+
*das Zusammenfassen von Anweisungssequenzen in Blöcken,
 +
*das Formulieren von alternativen Programmpfaden,
 +
*das Formulieren von Fallunterscheidungen und  
 +
*das wiederholte Durchführen von Programmsequenzen.
  
 
== Anweisungssequenzen ==
 
== Anweisungssequenzen ==
Zeile 9: Zeile 12:
 
Für die Anwendung in Java, siehe: [[Anweisungssequenzen]]
 
Für die Anwendung in Java, siehe: [[Anweisungssequenzen]]
  
Eine imperative Programmiersprache notiert ihre Anweisungen hintereinander um die Intention auszudrücken, dass diese Anweisungen hintereinander durchgeführt werden sollen.
+
Eine [[Imperative Programmierung|imperative Programmiersprache]] notiert ihre '''Anweisungen''' hintereinander, um die Intention auszudrücken, dass diese Anweisungen hintereinander durchgeführt werden sollen.
  
Dies ist in funktionalen Programmiersprachen z.B. anders, in denen meistens die Sequenz der Ausführung von Programmteilen durch die passende Verschachtelung in Funktionsaufrufen implementiert wird.
+
Dies ist z.B. in [[Funktionale Programmierung|funktionalen Programmiersprachen]] anders. Hier wird meistens die Sequenz der Ausführung von Programmteilen durch die passende Verschachtelung in Funktionsaufrufen implementiert.
 
+
=== Beispiele ===
+
 
+
TODO: Verschieben in eigenen (Java-)Artikel
+
 
+
<source lang="java" collapse="false" title="Einfache Programmsequenz">
+
[...]
+
    int x = 5;
+
    x = x + 10;
+
    System.out.println(x);
+
[...]
+
</source>
+
 
+
Dieses Codefragment besteht aus 3 einzelnen Anweisungen, die sequenziell ausgeführt werden. Zuerst wird eine Variable vom Typ '''int''' deklariert und mit 5 initialisiert. Die nächste Anweisung weist der Variablen x den Wert von x plus 10, also 15 zu. Die dritte Anweisung ruft die Methode '''println''' der Standardausgabe '''out''' der Klasse '''System''' mit dem Wert von x auf. Die Semantik, dass diese drei Anweisungen hintereinander ausgeführt werden sollen und Abhängig von der Ausführung der vorangehenden Anweisungen sind, ist zwar durch ihre hintereinander geschriebene Notation für den Programmierer deutlich, aber um diese Eigenschaft eindeutig zu definieren, muss das Semikolon nach jeder Programmzeile verwendet werden<ref>Andere Programmiersprachen, wie z.B. JavaScript, erfordern dies nicht</ref>. Die Ausgabe des Programmes ist "15".
+
 
+
<source lang="java" collapse="false" title="Einzeilige Programmsequenz">
+
[...]
+
    float a = 10.0f; a = a + 5.0f; System.out.println(a);
+
[...]
+
</source>
+
 
+
Auch dieses Programmfragment besteht aus 3 einzelnen, hintereinander ausgeführten Anweisungen. Das starten einer neuen Zeile für eine neue Anweisung ist zwar der am weitesten verbreitete Codingstandard, ist jedoch nicht erforderlich. Das einzig wichtige ist die Trennung einzelner Anweisungen durch ein Semikolon. Die Ausgabe des Programmes ist "15.0".
+
 
+
<source lang="java" collapse="false" title="Fehlende Semikola">
+
[...]
+
    int x = 5
+
    x = x + 10
+
    System.out.println(x)
+
[...]
+
</source>
+
 
+
Dies ist kein gültiger Code, da die einzelnen Anweisungen nicht durch Semikola getrennt werden und der Compiler daher versucht, die 3 einzelnen Anweisungen als eine zusammenhängende zu interpretieren.
+
 
+
<source lang="java" collapse="false" title="Mehrzeilige Anweisung">
+
[...]
+
    int x
+
        = 5;
+
    x =
+
      x + 5;
+
[...]
+
</source>
+
 
+
Entsprechend sind diese beiden Zeilen Code legitim, jedoch sehr ungerne gesehen. Beide Anweisungen sind syntaktisch korrekt (Leerzeichen, Zeilenumbrücke und Tabs werden vom Compiler ignoriert) und werden durch ein Semikolon voneinander getrennt.
+
  
 
== Block ==
 
== Block ==
Zeile 60: Zeile 20:
 
Für die Anwendung in Java, siehe: [[Block]]
 
Für die Anwendung in Java, siehe: [[Block]]
  
Anweisungssequenzen, die in einem Block stehen, sollen dadurch als eine zusammenhängende Einheit gekennzeichnet werden. Meistens wird dadurch die Syntax einer Programmiersprache vereinfacht: Durch die Definition "Alles was in einem Block ist, gilt als ''eine'' Anweisung" muss somit nichtmehr zwischen mehreren Anweisungen oder einer einzelnen Anweisung unterscheiden werden. Dies sieht man häufig in Syntaxdiagrammen anderer Kontrollstrukturen wieder.
+
Anweisungssequenzen, die in einem '''Block''' stehen, sollen dadurch als eine zusammenhängende Einheit gekennzeichnet werden. Meistens wird dadurch die Syntax einer Programmiersprache vereinfacht: Durch die Definition "Alles, was in einem Block ist, gilt als ''eine'' Anweisung" muss somit nicht mehr zwischen mehreren Anweisungen oder einer einzelnen Anweisung unterscheiden werden. Dies findet man häufig in Syntaxdiagrammen anderer Kontrollstrukturen wieder.
 
+
=== Beispiele ===
+
 
+
TODO: Verschieben.
+
 
+
<source lang="java" collapse="false" title="Einleitendes Beispiel">
+
[...]
+
    int x = 5;
+
    { /* modify x and print */
+
        x = x + 10;
+
        System.out.println(x);
+
    }
+
    System.out.println(x);
+
[...]
+
</source>
+
 
+
Das obere Codefragment deklariert eine Variable x und fasst das Erhöhen dieser Variablen und ihre Ausgabe auf der Konsole in einem Block zusammen. In diesem Beispiel hat der Block keinen weiteren Nutzen als dem lesenden Programmierer die beiden Zeilen hervorzuheben. Die Ausgabe des Programmes ist daher zwei mal "15".
+
 
+
<source lang="java" collapse="false" title="Verdecken von Variablen">
+
[...]
+
    int x = 5;
+
    { /* shadow x and print */
+
        int x = 10;
+
        System.out.println(x);
+
    }
+
    System.out.println(x);
+
[...]
+
</source>
+
 
+
In diesem Beispiel wird die Variable x außerhalb des Blockes von der neu deklarierten Variablen innerhalb des Blockes überdeckt. Anders, als wenn man eine Variable innerhalb des gleichen Blockes mehrfach deklariert, ist dies kein Fehler! Wird innerhalb des Blockes auf eine Variable mit dem Namen x zugegriffen, so wird zuerst nach einer Variablen gesucht, die innerhalb des Blockes deklariert wurde. Findet sich keine solche, so wird nach einer Variable außerhalb des Blockes mit diesem Namen gesucht. Da die Variable x innerhalb des Blockes mit dem Wert x deklariert und initialisiert wurde, ist die erste Ausgabe des Programmfragmentes "10". Die zweite Ausgabe des Programmfragmentes findet außerhalb des Blockes statt, wo die innere Variable des Blockes nicht mehr sichtbar<ref>Sie ist ab diesem Moment sogar nicht mehr existent; Durch das öffnen eines weiteren Blockes in der gleichen Ebene wird diese Variable nicht wieder sichtbar!</ref> ist. Somit wird auf die Variable x mit dem Wert 5 zugegriffen und die 2. Ausgabe ist "5".
+
 
+
 
+
<source lang="java" collapse="false" title="Fehlerhafte Verwendung von Blöcken">
+
[...]
+
    { /* declare and print x */
+
        int x = 10;
+
        System.out.println(x);
+
    }
+
    System.out.println(x);
+
[...]
+
</source>
+
 
+
Dieses Codefragment würde nicht compilieren, da die Variable x außerhalb des Blockes nicht sichtbar ist. Sie müsste irgendwo außerhalb des Blockes, vor Aufruf der println()-Methode deklariert und initialisiert worden sein.
+
  
 
== Alternativen ==
 
== Alternativen ==
  
Für die Anwendung in Java, siehe: [[Alternativen]]
+
Für die Anwendung in Java, siehe: [[Alternative]]
  
Durch Alternativen wird eine Gabelung im Programmpfad signalisiert. An dieser Stelle kann, in Abhängigkeit einer Aussage, das Programm in einen von zwei Pfaden abzweigen. Der alternative Pfad kann dabei meistens auch nicht vorhanden sein, um ein Überspringen des anderen Pfades zu signalisieren. Relevant ist, dass das Programm nach Durchführung der Alternative immer an der gleichen Programmzeile weiterarbeitet.
+
Durch '''Alternativen''' wird eine Gabelung im Programmpfad signalisiert. An dieser Stelle kann, in Abhängigkeit von einer Aussage, das Programm in einen von zwei Pfaden abzweigen. Der alternative Pfad kann dabei auch nicht vorhanden sein, um ein Überspringen des anderen Pfades zu signalisieren. Relevant ist, dass das Programm nach Durchführung der Alternative immer an der gleichen Programmzeile weiterarbeitet.
 
+
[[if | Eigener Artikel: if]]
+
  
 
== Fallunterscheidungen mit switch-case ==
 
== Fallunterscheidungen mit switch-case ==
Zeile 117: Zeile 32:
 
Für die Anwendung in Java, siehe: [[Fallunterscheidung]]
 
Für die Anwendung in Java, siehe: [[Fallunterscheidung]]
  
Fallunterscheidungen sind im Grunde nur Alternativen mit verschiedenen Pfaden für jedes mögliche Ergebnis. Die meisten Fallunterscheidungen decken ebenso nicht alle möglichen Ergebnisse mit einem speziellen Programmpfad ab, sondern behandeln ein paar Spezialfälle und decken den Rest mit einem sogenannten '''default'''-Pfad ab. Ebenso ist meistens der Sprung zum Ende der Fallunterscheidung nach Abarbeitung eines Pfades '''nicht''' in der Syntax der Programmiersprache vorgesehen und muss deswegen mit einer besonderen Anweisung erzwungen werden. Dies hat seine Hintergründe in der Art und Weise, wie die meisten Compiler eine solche Kontrollstruktur in Maschinencode übersetzen.
+
'''Fallunterscheidungen''' sind im Grunde lediglich Alternativen mit verschiedenen Pfaden für jedes mögliche Ergebnis. Anstatt jedoch jedes mögliche Ergebnis mit einem speziellen Programmpfad abzudecken, werden meistens nur Spezialfälle behandelt und der Rest mit einem sogenannten '''default'''-Pfad abgedeckt. Der Sprung zum Ende der Fallunterscheidung nach Abarbeiten eines Pfades ist meistens '''nicht''' in der Syntax der Programmiersprache vorgesehen und muss deswegen mit einer besonderen Anweisung erzwungen werden. Dies hat seine Hintergründe in der Art und Weise, wie die meisten [[Compiler]] eine solche Kontrollstruktur in [[Maschinencode]] übersetzen.
 
+
[[switch-case | Eigener Artikel: switch-case]]
+
  
 
== Wiederholung mit Schleifen ==
 
== Wiederholung mit Schleifen ==
Zeile 125: Zeile 38:
 
Für die Anwendung in Java, siehe: [[Schleifen]]
 
Für die Anwendung in Java, siehe: [[Schleifen]]
  
Eine Schleife drückt die Intention aus, in Abhängigkeit einer Aussage, einen Teil des Programmes wiederholt ausführen zu wollen. Um die Häufigkeit der Wiederholung zu symbolisieren verwenden Programme in imperativen Programmiersprachen meistens sogenannte ''Zählvariablen'' um zu vermerken, wie häufig die Schleife schon durchlaufen wurde und vergleichen diese dann mit einem Zielwert um mit der Wiederholung abzubrechen.
+
Eine '''Schleife''' drückt die Intention aus, in Abhängigkeit einer Aussage einen Teil des Programms wiederholt auszuführen. Um die Häufigkeit der Wiederholung zu symbolisieren, verwenden Programme in imperativen Programmiersprachen meistens sogenannte ''Zählvariablen''. Diese vermerken, wie häufig die Schleife schon durchlaufen wurde und werden dann mit einem Zielwert verglichen, um gegebenenfalls mit der Wiederholung abzubrechen.
  
 
Unterschieden wird hierbei zwischen kopfgesteuerten, fußgesteuerten und elementgesteuerten Schleifen.
 
Unterschieden wird hierbei zwischen kopfgesteuerten, fußgesteuerten und elementgesteuerten Schleifen.
  
Kopfgesteuerte Schleifen werten ihre Schleifenbedingung zum Eintritt in die Schleife aus und entscheiden dann und vor dem Beginn jedes weiteren Durchlaufes, ob der Rumpf der Schleife wiederholt oder übersprungen werden soll.
+
[[while|Kopfgesteuerte Schleifen]] werten ihre Schleifenbedingung beim Eintritt in die Schleife aus und entscheiden dann, und vor dem Beginn jedes weiteren Durchlaufes, ob der Rumpf der Schleife ausgeführt oder übersprungen werden soll.
 
+
Fußgesteuerte Schleifen werten ihre Schleifenbedingung zum Verlassen der Schleife aus und entscheiden dann, ob zurück zum Anfang der Schleife gesprungen werden soll. Dadurch wird eine fußgesteuerte Schleife immer mindestens einmal ausgeführt.
+
 
+
Elementgesteuerte Schleifen verwenden meistens komplexe Datenstrukturen als Argument und iterieren über jedes Element dieser Datenstruktur um eine Operation auf diesem Element auszuführen. Manche Programmiersprachen unterstützen ausschließlich diese Form von Schleife und implementieren kopf- und fußgesteuerte Schleifen mit Zählern über die Iteration über Listen von 1 bis zum Zielwert.
+
 
+
In funktionalen Programmiersprachen muss eine wiederholge Ausführung eines Teilprogrammes meistens über eine sogenannte Listenfaltung geführt werden und beinhaltet meist eine genauere Semantik hinter der verwendeten Syntax. Sie kommen hierbei den elementgesteuerten Schleifen am nähesten.
+
  
[[Schleife | Eigener Artikel: Schleife]]
+
[[do-while|Fußgesteuerte Schleifen]] werten ihre Schleifenbedingung beim Verlassen der Schleife aus und entscheiden dann, ob zurück zum Anfang der Schleife gesprungen werden soll. Dadurch wird eine fußgesteuerte Schleife immer mindestens einmal ausgeführt.
  
== Fußnoten ==
+
[[for|Elementgesteuerte Schleifen]] verwenden meistens komplexe Datenstrukturen als Argument und iterieren über jedes Element dieser Datenstruktur, um eine Operation auf diesem auszuführen. Manche Programmiersprachen unterstützen ausschließlich diese Form von Schleife und implementieren kopf- und fußgesteuerte Schleifen mit Zählern über die Iteration von Listen von 1 bis zum Zielwert.
  
<references />
+
In funktionalen Programmiersprachen muss die wiederholte Ausführung eines Teilprogrammes meistens über eine sogenannte Listenfaltung geführt werden und beinhaltet meist eine genauere Semantik hinter der verwendeten Syntax. Sie kommen hierbei den elementgesteuerten Schleifen am nächsten.

Aktuelle Version vom 25. Oktober 2017, 14:59 Uhr

Um die formalen Eigenschaften eines Algorithmus in die syntaktischen Eigenschaften eines Programms zu überführen, ist es notwendig, die einzelnen Anweisungen eines Algorithmus miteinander zu verknüpfen.

In vielen Programmiersprachen, so auch in Java, gibt es dafür eine kleine Grundmenge an sogenannten Kontrollstrukturen, die den Fluss eines Programms steuern. Diese sind im speziellen

  • die sequenzielle Ausführung einzelner Programmanweisungen,
  • das Zusammenfassen von Anweisungssequenzen in Blöcken,
  • das Formulieren von alternativen Programmpfaden,
  • das Formulieren von Fallunterscheidungen und
  • das wiederholte Durchführen von Programmsequenzen.

Anweisungssequenzen

Für die Anwendung in Java, siehe: Anweisungssequenzen

Eine imperative Programmiersprache notiert ihre Anweisungen hintereinander, um die Intention auszudrücken, dass diese Anweisungen hintereinander durchgeführt werden sollen.

Dies ist z.B. in funktionalen Programmiersprachen anders. Hier wird meistens die Sequenz der Ausführung von Programmteilen durch die passende Verschachtelung in Funktionsaufrufen implementiert.

Block

Für die Anwendung in Java, siehe: Block

Anweisungssequenzen, die in einem Block stehen, sollen dadurch als eine zusammenhängende Einheit gekennzeichnet werden. Meistens wird dadurch die Syntax einer Programmiersprache vereinfacht: Durch die Definition "Alles, was in einem Block ist, gilt als eine Anweisung" muss somit nicht mehr zwischen mehreren Anweisungen oder einer einzelnen Anweisung unterscheiden werden. Dies findet man häufig in Syntaxdiagrammen anderer Kontrollstrukturen wieder.

Alternativen

Für die Anwendung in Java, siehe: Alternative

Durch Alternativen wird eine Gabelung im Programmpfad signalisiert. An dieser Stelle kann, in Abhängigkeit von einer Aussage, das Programm in einen von zwei Pfaden abzweigen. Der alternative Pfad kann dabei auch nicht vorhanden sein, um ein Überspringen des anderen Pfades zu signalisieren. Relevant ist, dass das Programm nach Durchführung der Alternative immer an der gleichen Programmzeile weiterarbeitet.

Fallunterscheidungen mit switch-case

Für die Anwendung in Java, siehe: Fallunterscheidung

Fallunterscheidungen sind im Grunde lediglich Alternativen mit verschiedenen Pfaden für jedes mögliche Ergebnis. Anstatt jedoch jedes mögliche Ergebnis mit einem speziellen Programmpfad abzudecken, werden meistens nur Spezialfälle behandelt und der Rest mit einem sogenannten default-Pfad abgedeckt. Der Sprung zum Ende der Fallunterscheidung nach Abarbeiten eines Pfades ist meistens nicht in der Syntax der Programmiersprache vorgesehen und muss deswegen mit einer besonderen Anweisung erzwungen werden. Dies hat seine Hintergründe in der Art und Weise, wie die meisten Compiler eine solche Kontrollstruktur in Maschinencode übersetzen.

Wiederholung mit Schleifen

Für die Anwendung in Java, siehe: Schleifen

Eine Schleife drückt die Intention aus, in Abhängigkeit einer Aussage einen Teil des Programms wiederholt auszuführen. Um die Häufigkeit der Wiederholung zu symbolisieren, verwenden Programme in imperativen Programmiersprachen meistens sogenannte Zählvariablen. Diese vermerken, wie häufig die Schleife schon durchlaufen wurde und werden dann mit einem Zielwert verglichen, um gegebenenfalls mit der Wiederholung abzubrechen.

Unterschieden wird hierbei zwischen kopfgesteuerten, fußgesteuerten und elementgesteuerten Schleifen.

Kopfgesteuerte Schleifen werten ihre Schleifenbedingung beim Eintritt in die Schleife aus und entscheiden dann, und vor dem Beginn jedes weiteren Durchlaufes, ob der Rumpf der Schleife ausgeführt oder übersprungen werden soll.

Fußgesteuerte Schleifen werten ihre Schleifenbedingung beim Verlassen der Schleife aus und entscheiden dann, ob zurück zum Anfang der Schleife gesprungen werden soll. Dadurch wird eine fußgesteuerte Schleife immer mindestens einmal ausgeführt.

Elementgesteuerte Schleifen verwenden meistens komplexe Datenstrukturen als Argument und iterieren über jedes Element dieser Datenstruktur, um eine Operation auf diesem auszuführen. Manche Programmiersprachen unterstützen ausschließlich diese Form von Schleife und implementieren kopf- und fußgesteuerte Schleifen mit Zählern über die Iteration von Listen von 1 bis zum Zielwert.

In funktionalen Programmiersprachen muss die wiederholte Ausführung eines Teilprogrammes meistens über eine sogenannte Listenfaltung geführt werden und beinhaltet meist eine genauere Semantik hinter der verwendeten Syntax. Sie kommen hierbei den elementgesteuerten Schleifen am nächsten.