Zum Inhalt

Kontrollstrukturen

Einfache Programme bestehen aus der reinen Aneinanderreihung von Anweisungen. Mit Kontrollstrukturen kann die Ausführung von Anweisungen gesteuert werden, sodass beliebige Wiederholungen von Quellcodeblöcken (aka Schleifen, Loops) oder Verzweigungen (aka Bedingte Anweisung, If/Else Block) im Programmablauf realisiert werden können. Erst durch die Nutzung von Kontrollstrukturen lassen sich komplexere Algorithmen realisieren.

Programmablaufpläne

Programmablaufpläne (PAP) sind ein nützliches Hilfsmittel um Algorithmen oder Abläufe zu visualisieren und zu dokumentieren. Für Programmieranfänger:innen eignen sie sich um ein besseres Verständnis für Kontrollstrukturen in Programmiersprachen zu erlangen. Programme können mit unterschiedlichen Symbolen, welche jeweils eine entsprechende Bedeutung haben, grafisch beschrieben werden. Die Symbole für Programmablaufpläne sind sogar nach der DIN 66001 genormt. In der folgenden Tabelle finden sich die wesentlichen Symbole:

Symbol Bezeichnung Beschreibung
Eingabe, Ausgabe Eingabe, Ausgabe Mit dem Parallelogramm wird eine externe Eingabe oder eine Ausgabe des Programms dargestellt. Für ein Kommandozeilenprogramm würden die Python Funktionen input bzw. print mit diesem Symbol dargestellt werden.
Anweisung Anweisung Jede Aufgabe (zB Berechnung) die im Programm ausgeführt werden soll, wird über ein Rechteck dargestellt.
Verzweigung Verzweigung Mit der Raute wird eine Ja/Nein Entscheidung abgebildet. Der weitere Programmfluss folgt nur einem der ausgehenden Verbindungen.
Start/Stop Start/Stop Mit einem Oval oder Rechteck mit gerundeten Ecken wird ein Start- oder Stoppunkt des Programms definiert. Jedes Programm hat genau einen Startpunkt. Ein Programm hat mindestens einen Stoppunkt.
Verbindung Verbindung Mit Pfeilen wird eine Verbindung zwischen den einzelnen Elementen im PAP erzeugt. Die Verbindung spiegelt dabei den Programmablauf wieder.

PAP Beispiel: Fakultätsberechnung

Im folgenden Beispiel soll ein PAP für die Berechnung der mathematischen Fakultät erstellt werden. Das Programm soll auch über Ein- und Ausgabemöglichkeiten verfügen. Die mathematische Fakultät n! ist eine Funktion die einer natürlichen Zahl das Produkt aller natürlichen Zahlen (ohne Null) kleiner und gleich dieser Zahl zuordnet. Als Beispiel wäre die Fakultät der Zahl 5 also 5! über die Multiplikation 5 * 4 * 3 * 2 * 1 zu errechnen (5! = 120).

PAP Fakultät

PAP Beispiel: Größter gemeinsamer Teiler

Im folgenden Beispiel soll ein PAP für die Berechnung des größten gemeinsamen Teilers zweier natürlicher Zahlen erstellt werden. Das Programm soll auch über Ein- und Ausgabemöglichkeiten verfügen.

Als Algorithmus soll der euklidische Algorithmus zum Einsatz kommen. Der euklidische Algorithmus ist ein Algorithmus aus dem mathematischen Teilgebiet der Zahlentheorie. Das Verfahren ist nach dem griechischen Mathematiker Euklid benannt, der es in seinem Werk „Die Elemente“ beschrieben hat.

Der Algorithmus kann mit Strecken erklärt werden. Am Beispiel der Zahl 15 und 9. Man stelle sich vor es gibt die Strecke A und die Strecke B, die eine Länge von 15 bzw. 9 haben:

A |---------------|
B |---------|

Der Algorithmus geht nun iterativ so vor, dass immer die kürzere Strecke von der längeren Strecke abgezogen wird, solange bis beide Strecken gleich lang sind. Wenn die Strecken nach mehreren Substraktionen die gleiche Länge aufweisen, hat man den größten gemeinsamen Teiler errechnet. Für das Beispiel 15 und 9 würde dies folgendermaßen aussehen:

Start:
|---------------|
|---------|

1. Substraktion:
|---------|
|------|

2. Substraktion:
|------|
|---|

3. Substraktion:
|---|
|---|

Nach 3 Substraktionen würden also beide Strecken gleich lang sein und der größte gemeinsame Teiler der Zahlen 15 und 9 würde mit 3 feststehen. Als Programmablaufplan würde der Algorithmus folgendermaßen repräsentiert werden können:

PAP Euklidischer Algorithmus

Bedingte Anweisungen

Mit einer bedingten Anweisung kann der Programmablauf gesteuert werden. Dies wurde im PAP mit der Raute realisiert. Es wird also eine Bedingung definiert. Jenachdem ob die Bedingung wahr oder falsch ist, wird dem einen oder der anderen Zweig im Programmablauf gefolgt. In Python wird der sog. If/Else Block für Verzweigungen verwendet. Für die Formulierung von Bedingungen können Vergleichsoperationen oder logische Operationen verwendet werden.

Im folgenden Beispiel soll geprüft werden ob eine Zahl größer 10 ist oder nicht. Falls die Zahl größer 10 ist, soll der String "Die Zahl ist größer 10" ausgegben werden. Anderenfalls soll der String "Die Zahl ist nicht größer 10" ausgegeben werden:

number = int(input("Gib eine Zahl ein:"))

if number > 10:
    print("Die Zahl ist größer 10")
else:
    print("Die Zahl ist nicht größer 10")

Im Beispiel werden die Schlüsselwörter if und else für die Formulierung der bedingten Anweisung verwendet. Wichtig ist, dass nach dem Schlüsselwort if immer eine Bedingung stehen muss und auf die Bedingung immer der Doppelpunkt : folgt. Die Bedingung kann dabei beliebig komplex definiert sein, so könnten Vergleichsoperationen mit logischen Operationen beliebig verknüpft werden.

Falls die Bedingung wahr ist, würde der eingerückte Quellcodeabschnitt nach der if Zeile ausgeführt. Falls die Bedingung falsch ist, würde der eingerückte Quellcodeabschnitt nach der else Zeile ausgeführt. Da ein Wahrheitswert nur wahr oder falsch sein kann, muss für die else Zeile keine eigene Bedingung formuliert werden. Der else Abschnitt würde also immer dann ausgeführt werden, wenn die Bedingung der if Zeile falsch ist.

Eine bedingte Anweisung muss nicht zwingend einen else Zweig aufweisen. Wenn es nicht notwendig ist etwas auszuführen, wenn die Bedingung des if Zweiges falsch ist, kann der else Zweig ausgespart werden:

number = int(input("Gib eine Zahl ein:"))

if number > 10:
    print("Die Zahl ist größer 10")

print("Danke und auf Wiedersehen")

Mehrere Verzweigungen

Eine Verzweigung kann auch mehr als zwei Ausführungspfade erzeugen. Mit dem Schlüsselwort elif können alternative Bedingungen angegeben werden. Wichtig ist, dass die Bedingungen sequentiell von oben nach unten geprüft werden.

Die erste Bedingung die als wahr ausgewertet wird, bestimmt den Verlauf des Programms. Würden weitere Bedingungen ebenfalls als wahr ausgewertet werden, würden diese ignoriert werden.

Im folgenden Programm wird ein ersten einfaches Spiel entwickelt. Im Spiel soll die Geheimzahl geraten werden. Jenachdem ob die eingegebene Zahl kleiner, größer oder gleich der Geheimzahl ist, sollen unterschiedliche Ausgaben getätigt werden:

secret = 42
guess = int(input('Rate die Zahl:'))

if guess == secret:
    print('Sehr gut! Du hast die Zahl erraten')
elif guess < secret:
    print('Leider, die Geheimzahl ist größer als deine Eingabe')
else:
    print('Leider, die Geheimzahl ist kleiner als deine Eingabe')

Verzweigungen können grundsätzlich verschachtelt werden. Dies kann in manchen Fällen sinnvoll sein. Tiefe Verschachtelungen können auch die Lesbarkeit negativ beeinflussen, deshalb sollten Verschachtelungen sehr bedacht eingesetzt werden. Im folgenden Beispiel wird exemplarisch gezeigt, wie eine Verschachtelung für das Raten der Geheimzahl eingesetzt werden könnte:

secret = 42
guess = int(input('Rate die Zahl:'))

if guess == secret:
    print('Sehr gut! Du hast die Zahl erraten')
else:
    if guess < secret:
        print('Leider, die Geheimzahl ist größer als deine Eingabe')
    else: 
        print('Leider, die Geheimzahl ist kleiner als deine Eingabe')

Wiederholungen mit Schleifen

Mit einer Schleife kann ein Quellcodeabschnitt wiederholt ausgeführt werden. Die Wiederholung wird solange durchgeführt, solange die Schleifen-Bedingung als wahr ausgewertet wird. In Python werden Schleifen u.a. mit dem Schlüsselwort while realisert. Dabei gibt es den Schleifenkopf, welcher die Bedingung enthält und den eingerückten Schleifenkörper mit dem zu wiederholenden Quellcodeabschnitt.

Im folgenden Beispiel wird das bereits bekannte Ratespiel mittels einer Schleife realisiert. Die Bedingung der Schleife ist über guess != secret definiert. Die Variable guess wird dabei über die Benutzereingabe gesetzt. Dies bedeutet also, dass die Schleife solange wiederholt wird, bis die Benutzer:in die Zahl, welche in der Variable secret gesetzt ist, errät.

secret = 42
guess = 0

while guess != secret:
    guess = int(input("Rate die Zahl:"))

print("Super die Zahl wurde erraten!")

Neben der Bedingung im Schleifenkopf können für die Schleifensteuerung auch die beiden Schlüsselwörter continue und break herangezogen werden. Mit dem Schlüsselwort continue wird ein Schleifendurchlauf übersprungen und mit dem Schlüsselwort break wird die Schleife beendet.

Schleifenabbruch mit break

Neben der Bedingung im Schleifenkopf kann für die Schleifensteuerung auch das Schlüsselwort break verwendet werden um die Schleife frühzeitig zu beenden. Für das Ratespiel könnte es definiert sein, dass das Spiel durch die Eingabe einer negativen Zahl beendet wird. Falls die Benutzer:in nach langem Suchen zu keiner Lösung gekommen ist, könnte durch die Angabe einer negativen Zahl das Programm beendet werden:

secret = 42
guess = 0

while guess != secret:
    guess = int(input("Rate die Zahl:"))

    if guess < 0:
        break

print("Super die Zahl wurde erraten!")    

Innerhalb der Schleife wird eine Bedingung angegeben, welche prüft ob die Variable guess kleiner 0 ist. Falls dies der Fall ist, wird die Schleife mit dem Aufruf break beendet.

Letzter Schleifendurchlauf

Das Programm aus dem letzten Abschnitt weist eine kosmetische Ungereimtheit auf. Wenn das Ratespiel durch die Eingabe einer negativen Zahl abgebrochen wird, wird trotzdem die Ausgabe "Super die Zahl wurde erraten!" getätigt. Die Ausgabe sollte nur dann gemacht werden, wenn die Schleifenbedingung als falsch ausgewertet wird. Über einen else Zweig an der Schleife kann ein Anweisungsblock platziert werden, welcher nur ausgeführt wird, wenn die Schleifenbedingung als falsch ausgewertet wird. Der else Zweig an der Schleife wird nicht ausgeführt, wenn die Schleife über break abgebrochen wird.

secret = 42
guess = 0

while guess != secret:
    guess = int(input("Rate die Zahl:"))

    if guess < 0:
        break
else:
    print("Super die Zahl wurde erraten!")    

Schleifendurchlauf überspringen mit continue

Einzelne Schleifendurchläufe können mit dem Schlüsselwort continue übersprungen werden. Im folgenden Beispiel sollen alle Zahlen von 1 bis 30 ausgegeben werden, welche nicht durch 3 teilbar sind:

n = 0
while n < 30:
    n += 1

    if n % 3 == 0:
        continue

    print("{} ist eine Zahl die nicht durch 3 teilbar ist.".format(n))

Verschachtelte Schleifen

Schleifen können grundsätzlich auch beliebig verschatelt werden. Wichtig dabei ist vorallem, dass man die Lesbarkeit des enstehenden Quellcodes nicht außer Acht lässt. Im folgenden Quellcode soll ein Spielfeld für das Spiel TicTacToe erzeugt werden:

grid = ""

i = 0
while i < 3:
    grid += "+---+---+---+\n"
    i += 1

    j = 0
    while j < 3:
        grid += "|   "
        j += 1
    else:
        grid += "|\n"

else:
    grid += "+---+---+---+"

print(grid)

Die Ausgabe des obigen Quellcodes würde folgendes erzeugen:

+---+---+---+
|   |   |   |
+---+---+---+
|   |   |   |
+---+---+---+
|   |   |   |
+---+---+---+

For-Schleife

Die for-Schleife nutzt das sog. Iterator Design Pattern um zusammengesetzte Datenstrukturen sequentiell zu iterieren. Eine Zeichenkette ist ein Beispiel für eine zusammengesetzte Datenstruktur. Die Zeichenkette 'abcd' ist zusammengesetzt aus den 4 Elementen (Characters) 'a', 'b', 'c' und 'd'. Im folgenden Beispiel wird die for-Schleife genutzt um durch die Zeichenkette 'abcd' zu iterieren. Die for-Schleife wird dabei über die Schlüsselwörter for und in realisiert.

for char in 'abcd':
    print("Character: '{}'".format(char))

Python bietet die sehr nützliche built-in Funktion range. Mit dieser Funktion können Zählerschleifen realisiert werden. Mit range kann eine iterierbare Sequenz von Zahlen erzeugt werden, welche dann über die for-Schleife durchlaufen werden kann. Die range Funktion hat folgende Funktionsaufrufe definiert:

  • range(end): von 0 bis end-1
  • range(start, end):von start bis end-1
  • range(start, end, step):von start bis end-1 in der Schrittweite step

Im folgenden Beispiel wird mit range eine Zahlenfolgne von inklusive 10 bis exklusive 21 mit Schrittweite 2 definiert. Diese Zahlenfolge wird entsprechend mit der for-Schleife iteriert:

numbers = "" 

for i in range(10, 21, 2):
    numbers += str(i) + " "

print(numbers)  # 10 12 14 16 18 20

Whitespace Sensitivität

Die Programmiersprache Python ist Whitespace sensitiv. In Python wird Whitespace zur Gruppierung von Quellcodezeilen genutzt (zum Beispiel einen Schleifenkörper zu definieren). In anderen Programmiersprachen wie etwa Java, C oder JavaScript werden dazu geschweifte Klammern {} verwendet. In Python zwingt dies vorallem Programmieranfänger:innen Quellcodeeinrückungen durchzuführen. Quellcodeeinrückungen gelten generell als Good-Practice um die Lesbarkeit von Programmcode zu erhöhen.

Unter Softwareentwickler:innen sind die unterschiedlichen Stile und Einrückungsmöglichkeiten stark debatiert. Python gibt genau eine Form der Einrückung vor, dies führt bei Entwickler:innen immer wieder zu Kontroversen. Der Entwickler der Python Programmiersprache Guido van Rossum hat u.a. folgendes zur Einrückung von Programmabschnitten angemerkt:

Any individual creation has its ideosyncracies, and occasionally its creator has to justify these. Perhaps Python's most controversial feature is its use of indentation for statement grouping, which derives directly from ABC. It is one of the language's features that is dearest to my heart. It makes Python code more readable in two ways. First, the use of indentation reduces visual clutter and makes programs shorter, thus reducing the attention span needed to take in a basic unit of code. Second, it allows the programmer less freedom in formatting, thereby enabling a more uniform style, which makes it easier to read someone else's code. (Compare, for instance, the three or four different conventions for the placement of braces in C, each with strong proponents.)