Eine sehr vereinfachte Vorstellung eines Computerprogrammes ist ein Kochrezept. Zuerst werden die Zutaten (Daten) zurechtgelegt, dann anhand einer Liste von Befehlen bearbeitet, und zum Schluss gibt es ein hoffentlich bekömmliches Ergebnis.
Ein Computerprogramm ist aber mehr als eine lineare Abfolge von Befehlen. Üblicherweise werden anhand diverser Kriterien Entscheidungen getroffen, oder alle Elemente in einer Menge werden durch einen festen Handlungsablauf abgearbeitet, usw.
Das Konzept Kontrollstrukturen behandelt all diesen Steuerungselementen für so einen nichtlinearen Programmablauf.
Eine Verzweigung ist eine Stelle im Programmablauf, wo ein, zwei oder mehr alternative Wege wurzeln. Es wird maximal eine dieser Alternativen abgearbeitet! Am Schluss dieses Programmteils vereinigen sich diese Stränge (üblicherweise) wieder zu einem einzigen Handlungsablauf.
Schlüsselwörter:
if
: wenn der nachgestellte Wahrheitsausdruck (engl. "boolean expression") wahr ist, wird der nachfolgende Block abgearbeitet.else
: ist der Wahrheitsausdruck falsch, so springt die Ausführung in diesen optionalen else
Block.x = 4
if x > 0:
print("x ist positiv, ich kann die Wurzel ziehen!")
from math import sqrt
x = sqrt(x)
"x = %f" % x
x ist positiv, ich kann die Wurzel ziehen!
'x = 2.000000'
Quiz: Editiere vorhergehende Zelle so, dass x negativ ist und beobachte das Resultat.
y = 55
if y % 5 == 0:
k = y / 5
y += k
"y = %f" % y
'y = 66.000000'
Zusätzlich kann es vorkommen, dass Verzweigungen verschachtel werden; wie hier in Zeile 3 ein weiteres if
in Zeile 4 vorkommt:
a = 5
b = -3
if a > 0:
if b > 0:
x = a + b
else:
x = a - b
else:
x = b
"x = %f" % x
'x = 8.000000'
Gibt es mehr als zwei Alternativen,
wird diese if
-else
Struktur mit dem Schlüsselwort elif
erweitert.
Beispiel: Implementiere
\begin{equation} \operatorname{capped}(x) := \begin{cases} 1 & -1 \le x < 0 \\ \frac{1}{2} & x = 0 \\ 1 - x^2 & \text{otherwise} \end{cases} \end{equation}def capped(x):
if x == 0:
return 0
elif -1 <= x < 0:
return 1
else:
return 1 - x**2
capped(-4), capped(-2), capped(-1), capped(-.5), capped(0), capped(.5), capped(1), capped(3), capped(5)
(-15, -3, 1, 1, 0, 0.75, 0, -8, -24)
Bemerkung: Besonders praktisch ist der -1 <= x < 0
Ausdruck.
Er ließe sich auch als -1 <= x and x < 0
anschreiben.
Quiz: Angenommen, zwei Testausdrücke sind gleichzeitig wahr. Was passiert dann?
x = 10
if x % 2 == 0:
print("teilbar durch 2")
elif x > 0:
print("x ist positiv")
else:
print("x ist weder positiv, noch teilbar durch 2")
teilbar durch 2
... und was ist der Unterschied zu dem folgenden Beispiel?
x = 10
if x % 2 == 0:
print("teilbar durch 2")
if x > 0:
print("x ist positiv")
teilbar durch 2 x ist positiv
Neben Verzweigungen gibt es verschiedene Formen,
einen Codeblock nicht nur einmal, sondern öfters auszuführen.
Dies geschieht durch zwei verschiedene Schlüsselwörter: while
und for
:
while expr
: der Block wird so lange ausgeführt, so lange der Wahrheitsausdruck <expr>
wahr ist.for i in iterable
: das iterable
liefert nach und nach einzelne Elemente, welche der Reihe nach von i
angenommen werden.for i in collection
: für jedes i
in der abzuarbeitenden Menge wird der Block ausgeführt.
Dabei nimmt i
der Reihe nach jeden Wert der Sammlung an.x = 10
while x > 5:
print("x = %f" % x)
x = x * .95
x = 10.000000 x = 9.500000 x = 9.025000 x = 8.573750 x = 8.145062 x = 7.737809 x = 7.350919 x = 6.983373 x = 6.634204 x = 6.302494 x = 5.987369 x = 5.688001 x = 5.403601 x = 5.133421
Achtung: Ist der zu testende Ausdruck immer Wahr, wiederholt sich die Schleife "unendlich" oft. Das verträgt sich mit der endlichen Physik des Computers nicht, bzw. funktioniert das IPython Notebook im Webbrowser nicht mehr. Zum Abbrechen einer Berechnung muss man den Interrupt drücken.
Dies ist insbesondere dann problematisch, wenn sehr viel Output erzeugt wird. Daher sollte immer aufgepasst werden, dass solche Endlosschleifen nicht vorkommen, bzw. die Menge an möglichem Output gering gehalten wird.
Ein ebenfalls wichtiger Umstand ist,
dass Zuweisungen an die Schleifenvariable (hier i
) im sich wiederholenden Codeblock
bei jeder erneuten Wiederholung überschrieben werden.
for i in range(1, 10):
i = 42 * i**2
print("i = %6d" % i)
i = 42 i = 168 i = 378 i = 672 i = 1050 i = 1512 i = 2058 i = 2688 i = 3402
Gibt es nach der Schleife noch das i
und welchen Wert hat es dann?
i
3402
Will man neben dem zu iterierenden Element auch den Index in der Liste wissen,
so verwendet man den enumerate(...)
Iterator.
Er liefert pro Eintrag ein Paar ("tupel") zurück,
welches aus dem Index und dem Element besteht.
Durch "i, o
" wird das Tupel aufgetrennt und in i
und o
abgelegt.
obst = ["Apfel", "Tomate", "Birne", "Kiwi"]
for i, o in enumerate(obst):
print("{:2d}: {}".format(i, o))
0: Apfel 1: Tomate 2: Birne 3: Kiwi
Die Zählung startet wie üblich bei 0
.
Es können solche Iterationen vorzeitig mit dem Schlüsselwort break
abgebrochen werden.
Im folgenden Beispiel fehlt die "Kiwi"!
for o in obst:
print(o)
if o == "Birne":
break
Apfel Tomate Birne
Das Gegenstück zu dem break
Schlüsselwort ist continue
:
Wird dieses erreicht, springt die Ausführung vorzeitig an den Beginn der Schleife!
Beachte im folgenden Beispiel,
dass die durch 3 teilbaren Zahlen nicht aufscheinen.
for k in range(10):
if k % 3 == 0:
continue
print("k = %d" % k)
k = 1 k = 2 k = 4 k = 5 k = 7 k = 8
Umgekehrt kann man auch überprüfen,
ob man in einer Schleife vorzeitig abgebrochen hat oder nicht.
Im folgenden Beispiel wird nach dem Obst "Banane" gesucht, aber nicht gefunden.
Beachte, dass das else
Schlüsselwort auf selber Höhe wie das for
Schlüsselwort ist.
for o in obst:
print(o)
if o == "Banane":
print("Banane wurde gefunden")
break
else:
print("Die Banane wurde leider nicht gefunden")
Apfel Tomate Birne Kiwi Die Banane wurde leider nicht gefunden
Ebenso, für while-else
Schleifen. Der else:
-Fall tritt dann ein, wenn die logische Bedingung im Kopf der while
-Schleife falsch wird (und daher kein break
passiert ist). Reduziere die energie
auf 30, um den Unterschied zu sehen.
ziel = 42 # muss zu 42 kommen
position = 1 # startet bei 1
energie = 50 # energie für 30 schritte!
while position != ziel:
position += 1 # ein Schritt weiter
energie -= 1 # verbraucht eine Energieeinheit
if energie <= 0: # Abbruch, da keine Energie mehr
break
else:
print("Ziel gefunden! Restenergie: %s" % energie)
Ziel gefunden! Restenergie: 9
Schleifen und Verzweigungen lassen sich beliebig verschachteln.
Das bedeutet, innerhalb einer while
-Schleife können mehrere for
-Schleifen sein,
in denen wiederum if
-Verzweigungen vorkommen.
k = 0
while k < 1000:
for i in range(10):
if i % 5 == 4:
print("Addiere %d zu k=%d" % (i, k))
k += i
if k > 50:
for i in [1, 2]:
print("Setzen von k auf 2*(%d + %d)" % (k, i))
k = 2*(k + i)
print("Ergebnis: k = %d" % k)
Addiere 4 zu k=0 Addiere 9 zu k=4 Addiere 4 zu k=13 Addiere 9 zu k=17 Addiere 4 zu k=26 Addiere 9 zu k=30 Addiere 4 zu k=39 Addiere 9 zu k=43 Setzen von k auf 2*(52 + 1) Setzen von k auf 2*(106 + 2) Addiere 4 zu k=216 Addiere 9 zu k=220 Setzen von k auf 2*(229 + 1) Setzen von k auf 2*(460 + 2) Addiere 4 zu k=924 Addiere 9 zu k=928 Setzen von k auf 2*(937 + 1) Setzen von k auf 2*(1876 + 2) Ergebnis: k = 3756
Wichtig: Einrückungen sind hierbei entscheidend, welche Teile der Verschachtelungen wann ausgeführt werden. Beispiel für einen feinen, aber entscheidenden Unterschied in Zeile 4:
for k in [1, 2]:
for j in ["u", "v"]:
print(j)
print(k)
u 1 v 1 u 2 v 2
for k in [1, 2]:
for j in ["u", "v"]:
print(j)
print(k)
u v 1 u v 2
Nicht ganz ungewöhnlich sind "unendliche" Schleifen ohne vorgegebenem Ende.
Prominentester Vertreter ist eine while
-Schleife mit der Bedingung True
.
In solchen Fällen muss es Vorkehrungen geben, dass die Schleife nicht tatsächlich unendlich lange läuft ;-)
counter = 1
while True:
print("x" * counter)
counter += 1
if counter > 10: break
x xx xxx xxxx xxxxx xxxxxx xxxxxxx xxxxxxxx xxxxxxxxx xxxxxxxxxx
Auch bei for
-Schleifen in Kombination mit Iteratoren kann dies eintreten.
Hier ein Beispiel, wo ein zyklischer Iterator cycle
eine Liste immer wieder wiederholt.
from itertools import cycle
for counter, element in enumerate(cycle(obst)):
print("%3d: %s" % (counter, element))
if counter > 10: break
0: Apfel 1: Tomate 2: Birne 3: Kiwi 4: Apfel 5: Tomate 6: Birne 7: Kiwi 8: Apfel 9: Tomate 10: Birne 11: Kiwi