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