diff --git a/notebooks/seminar06.ipynb b/notebooks/seminar06.ipynb index e9982803f4d90c89734a1a8feb8b777fbb1f672b..910f18f45ffc35af9bf26486423b9ee7910b22e3 100644 --- a/notebooks/seminar06.ipynb +++ b/notebooks/seminar06.ipynb @@ -6,95 +6,59 @@ "source": [ "# Seminar Problemorientierte Programmierung\n", "\n", - "## Exkurs: Was mir an Python gefällt\n", + "## 6 Ertragreiche Funktionen\n", + "[Chapter 6: Fruitful function](http://greenteapress.com/thinkpython2/html/thinkpython2007.html)\n", "\n", - "In dieser Rubrik, die immer am Anfang eines Kapitels steht, möchte ich Ihnen zeigen, wofür ich Python nutze und warum ich es mag. Sie werden vielleicht noch nicht verstehen, was ich genau mache, aber Sie sehen damit schon einmal die Möglichkeiten von Python und können später darauf zurückgreifen. Da dies auch ein Exkurs ist, können Sie diese Rubrik gerne auch erst einmal überspringen.\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "\"\"\" \n", - "Quelle: https://teampython.wordpress.com/2015/12/12/print-a-christmas-tree/\n", - "Python 3 version by antiloquax (2015), based on code from datamungeblog.com.\n", - "\"\"\"\n", - " \n", - "from random import choice\n", - "from random import random\n", - " \n", - "# If you change this, use an odd number.\n", - "size = 21\n", - "\n", - "# Probability that a character will be green.\n", - "prob_gr = 0.6\n", - "# Colour codes.\n", - "colours = [31, 33, 34, 35, 36, 37]\n", - "# Characters to use for decorations. Experiment with these.\n", - "# The chr(169) and chr(174) characters may not work in all terminals\n", - "# (extended ASCII, c and r in a circle).\n", - "decs = ['@', '&', '*', chr(169), chr(174)]\n", - "\n", - "# Format string for printing blinking characters.\n", - "blink_col = \"\\033[5;{0}m{1}\\033[0m\"\n", - "# String to print a green octothorpe ('#').\n", - "leaf = \"\\033[32m#\\033[0m\"\n", - "\n", - "# Width of the tree, will grow by 2 each time.\n", - "width = 1\n", - "# Initialise the tree string, with a star at the top.\n", - "tree = \"\\n{}*\\n\".format(' ' * (size))\n", - "\n", - "\"\"\" Main Loop starts now.\"\"\"\n", - " \n", - "\"\"\" We can't use the normal \"format\" centering approach:\n", - " (\"{:^nn}\".format(string) where \"nn\" is the width of the line), \n", - " with these ansi codes. This is because Python sees the strings as being\n", - " more than one character long (15 & 10 for baubles and leaves).\"\"\"\n", - "\n", - "# Loop from (size - 1) down to 0, using the counter as the padding size.\n", - "for pad in range(size - 1, -1, -1):\n", - " # Increase the width of the tree by 2.\n", - " width += 2\n", - " \n", - " # Put the characters for the line in \"temp\".\n", - " temp = \"\"\n", - " for j in range(width):\n", - " # Make some leaves.\n", - " if random() < prob_gr:\n", - " temp += leaf\n", - " # And also some baubles.\n", - " else:\n", - " temp += blink_col.format(choice(colours), choice(decs))\n", - "\n", - " # Add that string to the line, with padding.\n", - " tree += \"{0}{1}\\n\".format(' ' * pad, temp)\n", - "\n", - "# Add a \"trunk\" of 2 lines and return.\n", - "print(tree + \"{0}{1}\\n\".format(' ' * (size - 1), \"000\") * 2)\n", - "print(\"\\x46\\x72\\x6f\\x68\\x65\\x20\\x46\\x65\\x73\\x74\\x74\\x61\\x67\\x65\\x21\")" + "\n", + "Viele Python-Funktionen die wir bis jetzt genutzt haben, wie z.B. die Mathematik-Funktionen aus dem `math`-Modul, erzeugen Rückgabewerte (*return values*). Aber die meisten Funktionen die wir selber geschrieben haben sind \"leer\": sie bewirken etwas, beispielsweise die Ausgabe eines Wertes (mit Hilfe der `print`-Funktion) oder die Bewegung einer Schildkröte, aber sie haben keinen Rückgabewert. In diesem Kapitel werden wir lernen, wie wir \"ertragreiche Funktionen\", also solche mit Rückgabewert, schreiben können.\n", + "\n", + "\n", + "\n", + "([Random Number](https://xkcd.com/221/), Randall Munroe)\n", + "\n", + "### Ihre Lernziele:\n", + "Beschreiben Sie in 2-3 Stichpunkten kurz was Sie im Seminar heute lernen wollen. Klicken Sie dazu doppelt auf diesen Text und bearbeiten Sie dann den Text:\n", + "\n", + " - \n", + " - \n", + " - " ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "Und noch viele weitere schöne Beispiele: https://codegolf.stackexchange.com/questions/15860/" + "## Exkurs: Was mir an Python gefällt\n", + "\n", + "Das Modul os stellt Funktionen bereit, um Funktionalitäten des Betriebssystems zu nutzen. Beispielsweise können wir damit Verzeichnis-Inhalte auflisten, durch Verzeichnisse navigieren, Informationen zu Dateien bekommen und Dateieigenschaften verändern. Das folgende Programm gibt eine Liste aller Jupyter-Notebooks im aktuellen Verzeichnis zusammen mit der Dateigröße aus und berechnet die Gesamtgröße der Dateien:" ] }, { - "cell_type": "markdown", + "cell_type": "code", + "execution_count": null, "metadata": {}, + "outputs": [], "source": [ - "## 6 Ertragreiche Funktionen\n", + "## Modul 6 \n", "\n", - "Viele Python-Funktionen die wir bis jetzt genutzt haben, wie z.B. die Mathematik-Funktionen aus dem `math`-Modul, erzeugen Rückgabewerte (*return values*). Aber die meisten Funktionen die wir bisher selber geschrieben haben sind \"leer\": sie bewirken etwas, beispielsweise die Ausgabe eines Wertes (mit Hilfe der `print`-Funktion) oder die Bewegung einer Schildkröte, aber sie haben keinen Rückgabewert. In diesem Kapitel werden wir lernen, wie wir \"ertragreiche Funktionen\", also solche mit Rückgabewert, schreiben können.\n", + "import os\n", "\n", - "\n", + "# Tabellenkopf ausgeben\n", + "print(\"Bytes\\tName\")\n", + "print(\"--------------------------------------------------------------\")\n", + "\n", + "# Gesamtgröße in Bytes\n", + "bytes_sum = 0\n", "\n", - "([Random Number](https://xkcd.com/221/), Randall Munroe)" + "# Inhalt des aktuellen Verzeichnisses durchlaufen\n", + "for entry in os.scandir():\n", + " if entry.is_file() and entry.name.endswith(\".ipynb\"):\n", + " size = entry.stat().st_size\n", + " bytes_sum +=size\n", + " print(\"{:5d}\".format(size), entry.name, sep='\\t')\n", + " \n", + "print(\"--------------------------------------------------------------\")\n", + "print(bytes_sum, \"bytes =\", bytes_sum/1000, \"kilobytes =\", bytes_sum/1000000, \"Megabytes\")" ] }, { @@ -171,7 +135,7 @@ "source": [ "Da solche `return`-Anweisungen in alternativen (sich gegenseitig ausschließenden) Zweigen liegen, wird nur eine davon ausgeführt.\n", "\n", - "Sobald eine `return`-Anweisung ausgeführt wird, wird die Funktion beendet, ohne die folgenden Anweisungen auszuführen. Code der nach einer `return`-Anweisung folgt oder an einer anderen Stelle, die während der Ausführung niemals erreicht werden kann, wird **toter Code** (*dead code*) genannt.\n", + "Sobald eine `return`-Anweisung ausgeführt wird, wird die Funktion beendet, ohne die folgenden Anweisungen auszuführen. Code, der nach einer `return`-Anweisung folgt oder an einer anderen Stelle, die während der Ausführung niemals erreicht werden kann, wird **toter Code** (*dead code*) genannt.\n", "\n", "\n", "\n", @@ -195,7 +159,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "Diese Funktion ist falsch, denn wenn `x` gleich 0 ist, ist keine der beiden Bedingungen erfüllt und die Funktion wird beendet, ohne dass eine `return`-Anweisung erreicht wird. Wenn die Ausführung das Ende einer Funktion erreicht, ist der Rückgabewert `None`, was nicht der Betrag von 0 ist:" + "Diese Funktion ist falsch, beziehungsweise unvollständig, denn wenn `x` gleich 0 ist, ist keine der beiden Bedingungen erfüllt und die Funktion wird beendet, ohne dass eine `return`-Anweisung erreicht wird. Wenn die Ausführung das Ende einer Funktion statt einer `return`-Anweisung erreicht, ist der Rückgabewert `None`, was nicht der Betrag von 0 ist:" ] }, { @@ -228,7 +192,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "Schreiben Sie eine Funktion `compare`, die zwei Parameter `x` und `y` erwartet und `1` zurückliefert, wenn `x > y` ist, `0` wenn `x == y` gilt und `-1` für `x < y`:" + "Schreiben Sie eine Funktion `compare`, die zwei Parameter `x` und `y` erwartet und `1` für `x > y`, `0` für `x == y` und `-1` für `x < y` zurückliefert:" ] }, { @@ -246,7 +210,7 @@ "source": [ "### 6.2 Schrittweise Entwicklung\n", "\n", - "Wenn Sie größere Funktionen schreiben kann es sein, dass Sie mehr Zeit mit der Fehlersuche (Debugging) verbringen.\n", + "Wenn Sie größere Funktionen schreiben, kann es sein, dass Sie mehr Zeit mit der Fehlersuche (Debugging) verbringen.\n", "\n", "Um mit zunehmend komplexeren Programmen klarzukommen, können Sie eine Methode verwenden, die sich **schrittweise Entwicklung** (*incremental development*) nennt. Das Ziel bei der schrittweisen Entwicklung ist die Vermeidung langer Fehlersuch-Sitzungen, indem immer nur kleine Codestücke hinzugefügt und getestet werden.\n", "\n", @@ -254,7 +218,7 @@ "\n", "$entfernung = \\sqrt{(x_2 - x_1)^2 + (y_2 - y_1)^2}$\n", "\n", - "Im ersten Schritt sollten wir uns überlegen, wie die Funktion `entfernung` in Python aussehen sollte. In anderen Worten: was sind die Eingaben (Parameter) und was ist das Ergebnis (Rückgabewert)?\n", + "Im ersten Schritt sollten wir uns überlegen, wie die Funktion `entfernung` in Python aussehen sollte. In anderen Worten: Was sind die Eingaben (Parameter) und was ist das Ergebnis (Rückgabewert)?\n", "\n", "In diesem Fall sind die Eingaben zwei Punkte, die wir durch vier Zahlen repräsentieren können. Das Ergebnis ist die Entfernung, repräsentiert als Gleitkommazahl.\n", "\n", @@ -299,7 +263,7 @@ "\n", "([Petrus3743](https://commons.wikimedia.org/wiki/File:01-Rechtwinkliges_Dreieck-Pythagoras.svg), Wikimedia Commons)\n", "\n", - "An dieser Stelle haben wir uns davon überzeugt, dass die Funktion syntaktisch korrekt ist und wir können damit beginnen, Code zum Rumpf hinzuzufügen. Ein naheliegender nächster Schritt ist, die Differenzen $x_2-x_1$ und $y_2-y_1$ zu berechnen. Die nächste Version speichert die Werte in temporären Variablen und gibt sie aus: " + "An dieser Stelle haben wir uns davon überzeugt, dass die Funktion syntaktisch korrekt ist. Wir können also damit beginnen, Code zum Rumpf hinzuzufügen. Ein naheliegender nächster Schritt ist, die Differenzen $x_2-x_1$ und $y_2-y_1$ zu berechnen. Die nächste Version speichert die Werte in temporären Variablen und gibt sie aus: " ] }, { @@ -322,7 +286,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "Wenn die Funktion richtig funktioniert, sollte `dx ist 3` und `dx ist 4` ausgegeben werden. Wenn dem so ist wissen wir, dass die Funktion die Argumente richtig erhalten hat und die erste Berechnung korrekt durchgeführt wurde. Falls nicht, gibt es nur wenige Zeilen, die wir überprüfen müssen.\n", + "Wenn die Funktion richtig funktioniert, sollte `dx ist 3` und `dx ist 4` ausgegeben werden. Wenn dem so ist, wissen wir, dass die Funktion die Argumente richtig erhalten hat und die ersten Berechnungen korrekt durchgeführt wurden. Falls nicht, gibt es nur wenige Zeilen Code, die wir überprüfen müssen.\n", "\n", "Als nächstes berechnen wir die Summe der Quadrate von `dx` und `dy`:" ] @@ -347,7 +311,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "Wieder rufen wir die Funktion mit bekannten Werten auf und prüfen das Ergebnis (das 25 sein sollte). Schließlich können wir die Funktion `math.sqrt` nutzen um das Ergebnis zu berechnen und zurückzugeben:" + "Wieder rufen wir die Funktion mit bekannten Werten auf und prüfen das Ergebnis (das 25 sein sollte). Abschließend können wir die Funktion `math.sqrt` nutzen um das Ergebnis zu berechnen und zurückzugeben:" ] }, { @@ -375,12 +339,12 @@ "\n", "Die endgültige Version der Funktion zeigt nichts an (gibt nichts auf dem Bildschirm aus), wenn sie ausgeführt wird; sie gibt nur einen Wert zurück. Die `print`-Anweisungen die wir zwischendurch geschrieben haben sind hilfreich für die Fehlersuche, aber sobald die Funktion funktioniert, sollten wir sie entfernen. Solcher Code wird **Hilfscode** (*scaffolding*) genannt, denn er hilft beim Schreiben des Programms aber ist nicht Teil des endgültigen Produkts.\n", "\n", - "Wenn Sie mit Programmieren beginnen, sollten sie jeweils nur ein bis zwei Zeilen auf einmal hinzufügen. Sobald Sie mehr Erfahrung gesammelt haben werden Sie merken, dass Sie größere Stücke Code auf einmal schreiben und testen. In jedem Fall kann Ihnen schrittweise Entwicklung viel Zeit bei der Fehlersuche ersparen.\n", + "Wenn Sie mit Programmieren beginnen, sollten sie jeweils nur ein bis zwei Zeilen auf einmal hinzufügen. Sobald Sie mehr Erfahrung gesammelt haben, werden Sie merken, dass Sie größere Stücke Code auf einmal schreiben und testen können. In jedem Fall kann Ihnen schrittweise Entwicklung viel Zeit bei der Fehlersuche ersparen.\n", "\n", "Die wichtigsten Punkte dieses Vorgehens sind:\n", - "1. Beginnen Sie mit einem funktionierenden Programm und führen Sie nur kleine, inkrementelle Änderungen durch. Sollte ein Fehler auftreten, so sollten Sie zu jedem Zeitpunkt eine gute Idee davon haben, wodurch er hervorgerufen wird.\n", - "2. Nutzen Sie Variablen, um Zwischenwerte zu speichern, so dass Sie diese ausgeben (`print`) und überprüfen können.\n", - "3. Sobald das Programm funktioniert sollten Sie Teile des Hilfscodes entfernen oder mehrere Anweisungen zu einer Verbundanweisung zusammenfügen, aber nur, wenn sich dadurch die Lesbarkeit des Programms nicht verschlechtert.\n", + "1. Beginnen Sie mit einem kleinen funktionierenden Programm und führen Sie nur inkrementelle (schrittweise) Änderungen durch. Falls ein Fehler auftritt, sollten Sie zu jeden Zeitpunkt eine Ahnung haben, in welcher Zeile der Fehler sich befindet.\n", + "2. Nutzen Sie Variablen, um Zwischenwerte zu speichern, so dass Sie diese mit `print` ausgeben und überprüfen können.\n", + "3. Sobald das Programm funktioniert, sollten Sie Teile des Hilfscodes entfernen und gegebenenfalls mehrere Anweisungen zu einer Verbundanweisung zusammenfügen, aber nur, wenn sich dadurch die Lesbarkeit des Programms nicht verschlechtert.\n", "\n", "**Übung:** Nutzen Sie das Prinzip der schrittweisen Entwicklung, um eine Funktion `hypothenuse` zu schreiben, die die Länge der Hypothenuse eines rechtwinkligen Dreiecks zurückgibt, wenn die Längen der beiden Katheden gegeben sind. Dokumentieren Sie jeden Entwicklungsschritt hier im Notebook (d.h., erzeugen Sie eine Kopie der Funktion, bevor Sie den nächsten Entwicklungsschritt durchführen)." ] @@ -606,17 +570,17 @@ "source": [ "### 6.5 Noch mehr Rekursion\n", "\n", - "Wir haben bisher nur eine kleine Teilmenge von Python kennengelernt aber vielleicht interessiert es Sie zu wissen, dass diese Teilmenge eine *komplette* Programmiersprache darstellt. Das heißt, alles was berechnet werden kann, können wir mit den bisher erlernten Anweisungen und Funktionen ausdrücken! Jedes jemals geschriebene Programm könnten wir umschreiben, so dass es nur mit den Sprachmerkmalen auskommt, die wir bis jetzt gelernt haben (gut, wir bräuchten noch ein paar Anweisungen um Geräte wie z.B. die Maus, Festplatten, etc. zu kontrollieren).\n", + "Wir haben bisher nur eine kleine Teilmenge von Python kennengelernt aber vielleicht interessiert es Sie zu wissen, dass diese Teilmenge eine *komplette* Programmiersprache darstellt. Das heißt, alles was berechnet werden kann, können wir mit den bisher erlernten Anweisungen und Funktionen ausdrücken! Jedes jemals geschriebene Programm könnten wir umschreiben, so dass es nur mit den Sprachmerkmalen auskommt, die wir bis jetzt gelernt haben (gut, wir bräuchten noch ein paar Anweisungen, um Geräte wie z.B. die Maus, Festplatten, etc. zu kontrollieren).\n", "\n", "\n", "\n", - "Diese Behauptung zu beweisen ist eine nicht so ganz einfache Aufgabe, die zuerst von [Alan Turing](https://de.wikipedia.org/wiki/Alan_Turing) gelöst wurde. Er war einer der ersten Informatiker (einige würden argumentieren, dass er ein Mathematiker war, aber viele der ersten Informatiker begannen als Mathematiker). Dementsprechend wird dies oft als [Turing-These](https://de.wikipedia.org/wiki/Church-Turing-These) bezeichnet. \n", + "Diese Behauptung zu beweisen, ist eine nicht so ganz einfache Aufgabe, die zuerst von [Alan Turing](https://de.wikipedia.org/wiki/Alan_Turing) gelöst wurde. Er war einer der ersten Informatiker (einige würden argumentieren, dass er ein Mathematiker war, aber viele der ersten Informatiker begannen als Mathematiker). Dementsprechend wird dies oft als [Turing-These](https://de.wikipedia.org/wiki/Church-Turing-These) bezeichnet. \n", "\n", - "Um einen Idee davon zu bekommen, was wir mit den Werkzeugen, die wir bisher kennengelernt haben, schon erreichen können, wollen wir einige rekursiv definierte mathematische Funktionen implementieren. Eine rekursive Definition ist ähnlich einer [zirkulären Definition](https://en.wikipedia.org/wiki/Circular_definition) (*circular definition* - leider konnte ich dafür keinen deutschen Begriff finden) in dem Sinne, dass die Definition eine Referenz auf das was definiert wird enthält. Eine richtig zirkuläre Definition ist nicht sehr nützlich:\n", + "Um einen Idee davon zu bekommen, was wir mit den Werkzeugen, die wir bisher kennengelernt haben, schon erreichen können, wollen wir einige rekursiv definierte mathematische Funktionen implementieren. Eine rekursive Definition ist ähnlich einer [zirkulären Definition](https://en.wikipedia.org/wiki/Circular_definition) (*circular definition* - leider konnte ich dafür keinen deutschen Begriff finden) in dem Sinne, dass die Definition eine Referenz auf das, was definiert wird, enthält. Eine richtig zirkuläre Definition ist nicht sehr nützlich:\n", "\n", "**vorpal:** Ein Adjektiv welches genutzt wird, um etwas zu beschreiben, was vorpal ist.\n", "\n", - "Wenn Sie so eine Definition in einem Wörterbuch sehen, sind sie vermutlich verärgert. Andererseits, wenn wir uns die Definition der Fakultätsfunktion heraussuchen (die mit dem Symbol ! bezeichnet wird), finden wir vermutlich etwas in der Art:\n", + "Wenn Sie so eine Definition in einem Wörterbuch sehen, sind Sie vermutlich verärgert. Andererseits, wenn wir uns die Definition der Fakultätsfunktion heraussuchen (die mit dem Symbol ! bezeichnet wird), finden wir vermutlich etwas in der Art:\n", "\n", "\\begin{align}\n", "0! &= 1\\\\\n", @@ -699,7 +663,9 @@ "\n", "Das Diagramm zeigt, wie die Rückgabewerte im Stapel weiter nach oben durchgereicht werden. In jedem Block ist der Rückgabewert der Wert von `ergebnis`, was das Produkt von `n` und `rekursion` ist.\n", "\n", - "Im untersten (letzten) Block existieren die lokalen Variablen `rekursion` und `ergebnis` nicht, denn derjenige Zweig, welcher diese erzeugt, wird nicht ausgeführt." + "Im untersten (letzten) Block existieren die lokalen Variablen `rekursion` und `ergebnis` nicht, denn derjenige Zweig, welcher diese erzeugt, wird nicht ausgeführt.\n", + "\n", + "Nutzen Sie auch http://pythontutor.com/, um die einzelnen Schritte nachzuvollziehen!" ] }, { @@ -710,11 +676,11 @@ "\n", "Dem Kontrollfluss zu folgen ist eine Möglichkeit, Programme zu lesen, aber das kann ganz schön aufwendig sein. Eine Alternative ist, dem Code einen \"Vertrauensvorschuss\" zu geben. Wenn wir einen Funktionsaufruf sehen, können wir, statt dem Kontrollfluss zu folgen, einfach *annehmen*, dass die Funktion richtig arbeitet und das korrekte Ergebnis zurückliefert.\n", "\n", - "Tatsächlich praktizieren wir das bisher schon mit den eingebauten Funktionen. Wenn wir `math.cos` oder `print` aufrufen, schauen wir uns den Rumpf dieser Funktionen nicht an. Wir gehen einfach davon aus, dass sie funktionieren, weil die Leute, die sie geschrieben haben, gute Programmierer/innen sind. (Zumindest nehmen wir das vielleicht an ;-)\n", + "Tatsächlich praktizieren wir das bisher schon mit den eingebauten Funktionen. Wenn wir `math.cos` oder `print` aufrufen, schauen wir uns den Rumpf dieser Funktionen nicht an. Wir gehen einfach davon aus, dass sie funktionieren, weil die Leute, die sie geschrieben haben, gute Programmierer/innen sind. (Zumindest nehmen wir das vielleicht an ;-) )\n", "\n", - "Das gleiche gilt wenn wir eine unserer eigenen Funktionen aufrufen. Beispielsweise haben wir in [Abschnitt 6.4](#6.4-Boolesche-Funktionen) eine Funktion `ist_teilbar` geschrieben, die bestimmt, ob eine Zahl durch eine andere teilbar ist. Sobald wir uns davon überzeugt haben, dass diese Funktion korrekt arbeitet - durch Verstehen des Codes und Testen - können wir die Funktion nutzen, ohne uns den Rumpf noch einmal anzuschauen.\n", + "Das gleiche gilt, wenn wir eine unserer eigenen Funktionen aufrufen. Beispielsweise haben wir in [Abschnitt 6.4](#6.4-Boolesche-Funktionen) eine Funktion `ist_teilbar` geschrieben, die bestimmt, ob eine Zahl durch eine andere teilbar ist. Sobald wir uns davon überzeugt haben, dass diese Funktion korrekt arbeitet - durch Verstehen des Codes und Testen - können wir die Funktion nutzen, ohne uns den Rumpf noch einmal anzuschauen.\n", "\n", - "Das gleiche gilt für rekursive Programme. Wenn wir auf einen rekursiven Funktionsaufruf treffen, können wir, anstatt dem Kontrollfluss zu folgen, annehmen, das der rekursive Aufruf funktioniert (also den richtigen Wert zurückliefert) und uns selbst beispielsweise fragen \"Angenommen, ich kann die Fakultät von $n-1$ berechnen, kann ich dann die Fakultät von $n$ berechnen?\" Das funktioniert offensichtlich - indem wir mit $n$ multiplizieren.\n", + "Das gleiche gilt für rekursive Programme. Wenn wir auf einen rekursiven Funktionsaufruf treffen, können wir, anstatt dem Kontrollfluss zu folgen, annehmen, dass der rekursive Aufruf funktioniert (also den richtigen Wert zurückliefert) und uns selbst beispielsweise fragen \"Angenommen, ich kann die Fakultät von $n-1$ berechnen, kann ich dann die Fakultät von $n$ berechnen?\" Das funktioniert offensichtlich - indem wir mit $n$ multiplizieren.\n", "\n", "Natürlich ist es etwas seltsam, anzunehmen, dass die Funktion richtig arbeitet, wenn wir sie noch nicht fertig implementiert haben, aber daher wird das ganze ja auch Vertrauensvorschuss genannt. " ] @@ -854,9 +820,9 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "Wenn wir beide Überprüfungen \"bestehen\", dann wissen wir, dass `n` eine positive ganze Zahl ist (oder Null). Damit können wir zeigen, dass die Rekursion terminiert.\n", + "Wenn wir beide Überprüfungen \"bestehen\", dann wissen wir, dass `n` eine positive ganze Zahl oder Null ist. Damit können wir zeigen, dass die Rekursion terminiert.\n", "\n", - "Dieses Programm demonstriert ein Entwurfsmuster welches manchmal **Wächter** (*guardian*) genannt wird. Die ersten beiden Verzweigungen agieren als Wächter, die den darauffolgenden Code vor Werten beschützen, die Fehler hervorrufen könnten. Die Wächter ermöglichen uns, die Korrektheit des Codes zu beweisen.\n", + "Dieses Programm demonstriert ein Entwurfsmuster, welches manchmal **Wächter** (*guardian*) genannt wird. Die ersten beiden Verzweigungen agieren als Wächter, die den darauffolgenden Code vor Werten beschützen, die Fehler hervorrufen könnten. Die Wächter ermöglichen uns, die Korrektheit des Codes zu beweisen.\n", "\n", "Im [Abschnitt 11.4](seminar11.ipynb#reverse-lookup) werden wir eine flexiblere Alternative kennenlernen, um eine Fehlermeldung auszugeben: Ausnahmebehandlung." ] @@ -877,7 +843,7 @@ "\n", "Wenn die Parameter gut aussehen, dann können wir eine `print`-Anweisung vor jeder `return`-Anweisung einfügen und den Rückgabewert anzeigen. Falls möglich, prüfen wir den Wert von Hand. Wir können auch in Betracht ziehen, die Funktion mit Werten aufzurufen, die uns das überprüfen des Ergebnisses erleichtern (wie in [Abschnitt 6.2](#6.2-Schrittweise-Entwicklung)). \n", "\n", - "Wenn die Funktion richtig arbeitet (oder es danach ausschaut), sollten wir uns die Stelle anschauen, wo die Funktion aufgerufen wird und sicherstellen, dass der Rückgabewert richtig verwendet wird (bzw. überhaupt verwendet wird!).\n", + "Wenn die Funktion richtig arbeitet (oder es zumindest danach aussieht), sollten wir uns die Stelle anschauen, an der die Funktion aufgerufen wird und sicherstellen, dass der Rückgabewert richtig bzw. überhaupt verwendet wird.\n", "\n", "Das Hinzufügen von `print`-Anweisungen am Anfang und Ende einer Funktion kann uns helfen, den Kontrollfluss besser sichtbar zu machen. Hier ist beispielsweise eine Version von `fakultaet` mit `print`-Anweisungen:" ] @@ -898,7 +864,8 @@ " rekursion = fakultaet(n-1)\n", " ergebnis = n * rekursion\n", " print(space, 'returning', ergebnis)\n", - " return ergebnis" + " return ergebnis\n", + " " ] }, { @@ -940,7 +907,7 @@ "- toter Code:\n", "- schrittweise Entwicklung\n", "- Hilfscode:\n", - "- Wächter:\n", + "- Wächter: Auch *guardian* genannt, beschützt der \"Wächter\" darauf folgenden Code vor Eingaben, die zu Fehlern führen können.\n", "\n", "Ergänzen Sie die Liste in eigenen Worten. Das ist eine gute Erinnerungs- und Übungsmöglichkeit." ] @@ -998,7 +965,7 @@ "\\end{cases}\n", "\\end{equation}\n", "\n", - "Schreiben Sie eine Funktion `ack` die die Ackermannfunktion berechnet. Berechnen Sie mit ihrer Funktion `ack(3,4)`, was 125 ergeben sollte. Was passiert für größere Werte von `m` und `n`? Lösung: http://thinkpython2.com/code/ackermann.py" + "Schreiben Sie eine Funktion `ack` die die Ackermannfunktion berechnet. Berechnen Sie mit ihrer Funktion `ack(3,4)`, was 125 ergeben sollte. Was passiert für größere Werte von `m` und `n`? " ] }, { @@ -1014,6 +981,47 @@ "ack(3,4)" ] }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "<a data-flickr-embed=\"true\" href=\"https://www.flickr.com/photos/jasoneppink/4964471335\" title=\"Spoiler Alert\"><img src=\"https://farm5.staticflickr.com/4110/4964471335_1f86a923f3_n.jpg\" width=\"320\" height=\"213\" alt=\"Spoiler Alert\"></a><script async src=\"//embedr.flickr.com/assets/client-code.js\" charset=\"utf-8\"></script>\n", + "\n", + "(Quelle: Jason Eppink, Flickr)\n", + "\n", + "1. Wie muss die Grundstruktur der Funktion aussehen, was ist der Kopf, was sind die Parameter?\n", + "2. Wie ist die Funktion aufgebaut, wie viele Zweige hat sie? Schreiben Sie die leeren Zweige mit `return`-Anweisungen auf.\n", + "3. Was sind die Bedingungen für die einzelnen Zweige, übergeben Sie diese den verschiedenen `if`-Anweisungen. \n", + "4. Was wird im Basisfall $m=0$ zurückgegeben? Schreiben Sie die `return`-Anweisung für diesen Zweig.\n", + "5. Was passiert wenn n gleich null ist? Mit welchen Werten wird ack aufgerufen? Geben Sie den neuen Aufruf hinter der `return`-Anweisung ein.\n", + "6. Was passiert im letzten Zweig? Hier wird es ein wenig kompliziert, aber im Prinzip müssen sie den Ausdruck aus der Formel oben nur hinter der `return`-Anweisung abschreiben\n", + "7. Vielleicht wollen Sie jetzt noch Wächter einbauen oder eine `Doc-String` schreiben, die dem Nutzer klar sagt, für welche Zahlen `ack` definiert ist. \n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "def ack(m,n):\n", + " if m<0 or n<0:\n", + " print(\"Funktion nicht definiert\")\n", + " return None\n", + " if not isinstance (n, int) or not isinstance (m, int):\n", + " print (\"Funktion nicht definiert\")\n", + " return None\n", + " if m==0:\n", + " return n+1\n", + " if n==0 and m>0:\n", + " return ack (m-1,1)\n", + " if m>0 and n>0:\n", + " return ack (m-1, ack(m, n-1))\n", + " \n", + " \n", + "ack(3,4)" + ] + }, { "cell_type": "markdown", "metadata": {}, @@ -1048,9 +1056,45 @@ "Wir werden in [Kapitel 8](seminar08.ipynb) sehen, wie sie funktionieren.\n", "\n", "1. Testen Sie diese Funktionen. Was passiert, wenn Sie `middle` mit einer Zeichenkette mit nur zwei Zeichen aufrufen? Oder mit nur einem Zeichen? Was passiert mit der leeren Zeichenkette, geschrieben '', die keine Zeichen enthält?\n", - "2. Schreiben Sie eine Funktion `ist_palindrom`, die eine Zeichenkette als Argument erwartet und `True` zurückliefert, wenn die Zeichenkette ein Palindrom ist und ansonsten `False`. (Erinnern Sie sich daran, dass Sie mit der eingebauten Funktion `len` die Länge einer Zeichenkette ermitteln können.)\n", + "2. Schreiben Sie eine Funktion `ist_palindrom`, die eine Zeichenkette als Argument erwartet und `True` zurückliefert, wenn die Zeichenkette ein Palindrom ist und ansonsten `False`. (Erinnern Sie sich daran, dass Sie mit der eingebauten Funktion `len` die Länge einer Zeichenkette ermitteln können.)\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Testen Sie hier die Funktionen first, last und middle " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Implementieren Sie die Funktion ist_palindrom" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ "\n", - "Lösung: http://thinkpython2.com/code/palindrome_soln.py" + "<a data-flickr-embed=\"true\" href=\"https://www.flickr.com/photos/jasoneppink/4964471335\" title=\"Spoiler Alert\"><img src=\"https://farm5.staticflickr.com/4110/4964471335_1f86a923f3_n.jpg\" width=\"320\" height=\"213\" alt=\"Spoiler Alert\"></a><script async src=\"//embedr.flickr.com/assets/client-code.js\" charset=\"utf-8\"></script>\n", + "\n", + "(Quelle: Jason Eppink, Flickr)\n", + "\n", + "1. Verwenden Sie die Funktionen first, last und middle.\n", + "2. Überlegen Sie wann ein Palindrom ein Palindrom ist und wie sie das einfach testen können.\n", + "3. Wenn Sie von außen nach innen immer Zeichenpaare vergleichen und diese Stimmen miteinander überein, dann haben Sie ein Palindrom.\n", + "4. Wir wollen das ganze rekursiv implementieren, was ist dabei der Basisfall? \n", + "5. Eine Zeichenkette mit einem oder keinem Zeichen ist immer ein Palindrom und damit der Basisfall und einer der Zweige in denen die Funktion abbricht und einen Wert- nämlich `True`zurückgibt. Wir können die Länge der Zeichenkette mit der `len` Funktion testen.\n", + "6. Wann sonst bricht die Funktion ab, gibt aber `False` zurück?\n", + "7. Wenn das erste und letzte Zeichen, der momentanen Zeichenkette nicht übereinstimmen\n", + "8. Wir rufen `ist_palindrom` rekursiv mit `middle` auf um die Zeichenkette ohne den ersten und letzten Buchstaben zu erhalten, damit wird die Zeichenkette immer kürzer und bricht entweder ab, weil sie zu kurz ist- dann ist es ein Palindrom- oder weil das erste und letzte Zeichen nicht übereinstimmen - dann ist es kein Palindrom \n", + "\n" ] }, { @@ -1059,8 +1103,14 @@ "metadata": {}, "outputs": [], "source": [ - "# Testen Sie hier die Funktionen first, last und middle \n", - "# und implementieren Sie die Funktion ist_palindrom." + "def ist_palindrom(s):\n", + " if len(s)<=1:\n", + " return True\n", + " elif first(s)!=last(s): \n", + " return False\n", + " return ist_palindrom(middle(s))\n", + " \n", + "ist_palindrom(\"gohangasalamiimalasagnahog\")" ] }, { @@ -1085,6 +1135,42 @@ "# Implementieren Sie hier die Funktion ist_potenz" ] }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "\n", + "<a data-flickr-embed=\"true\" href=\"https://www.flickr.com/photos/jasoneppink/4964471335\" title=\"Spoiler Alert\"><img src=\"https://farm5.staticflickr.com/4110/4964471335_1f86a923f3_n.jpg\" width=\"320\" height=\"213\" alt=\"Spoiler Alert\"></a><script async src=\"//embedr.flickr.com/assets/client-code.js\" charset=\"utf-8\"></script>\n", + "\n", + "(Quelle: Jason Eppink, Flickr)\n", + "\n", + "1. Schreiben Sie erst den Kopf der Funktion inklusive Parametern\n", + "2. Bevor Sie den Rest der Funktion implementieren, müssen Sie sich den Basisfall überlegen.\n", + "3. Der Basisfall ist b=1 -- und darausfolgend auch a=b\n", + "4. Was ist die Ausgabe, wenn der Basisfall erreicht wird?\n", + "5. Was müssen Sie überprüfen um herauszufinden ob $a$ eine Potenz von $b$ ist?\n", + "6. Wenn $a$ nicht restlos durch $b$ teilbar ist, kann $a$ keine Potenz von $b$ sein. Implementieren Sie diese Aussage\n", + "7. Wie und wo muss die Funktion sich selber aufrufen?\n", + "8. Die Funktion muss sich !mit `return`-Anweisung! selber aufrufen wenn a%b==0 gilt\n", + "9. Dabei wird $a/b$ für $a$ übergeben." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "def ist_potenz(a,b):\n", + " if b==1 or a==b:\n", + " return True\n", + " if a%b==0:\n", + " return ist_potenz (a/b, b) \n", + " else:\n", + " return False\n", + "ist_potenz(12,3)" + ] + }, { "cell_type": "markdown", "metadata": {}, @@ -1107,6 +1193,39 @@ "# Implementieren Sie hier die Funktion ggt\n" ] }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "\n", + "<a data-flickr-embed=\"true\" href=\"https://www.flickr.com/photos/jasoneppink/4964471335\" title=\"Spoiler Alert\"><img src=\"https://farm5.staticflickr.com/4110/4964471335_1f86a923f3_n.jpg\" width=\"320\" height=\"213\" alt=\"Spoiler Alert\"></a><script async src=\"//embedr.flickr.com/assets/client-code.js\" charset=\"utf-8\"></script>\n", + "\n", + "(Quelle: Jason Eppink, Flickr)\n", + "\n", + "1. Wie muss der Kopf der Funktion aussehen, welche Parameter werden übergeben?\n", + "2. Was müssen wir für den Basisfall überprüfen? Schreiben Sie die passende `if`-Bedingung\n", + "3. Was wird im Basisfall zurück gegeben? Schreiben Sie die `return`-Anweisung\n", + "4. Wie können Sie den Rest der Division von $a/b$ berechnen?\n", + "5. Rufen Sie die Funktion rekursiv auf, übergeben Sie dabei die passenden Varibablen und vergessen Sie die `return`-Anweisung dabei nicht." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "def ggt (a,b):\n", + " if b==0:\n", + " return a\n", + " r=a%b\n", + " return ggt(b,r)\n", + " \n", + " \n", + "ggt (175,25)\n", + " \n" + ] + }, { "cell_type": "markdown", "metadata": {}, diff --git a/notebooks/seminar07.ipynb b/notebooks/seminar07.ipynb index 2086e332077d3de071575edc1b3b4221b80376fb..550bd18c56ed06900d84e9afb402a910c2d1a2ed 100644 --- a/notebooks/seminar07.ipynb +++ b/notebooks/seminar07.ipynb @@ -6,82 +6,103 @@ "source": [ "# Seminar Problemorientierte Programmierung\n", "\n", - "## Exkurs: Was mir an Python gefällt\n", + "## 7 Iteration\n", "\n", - "In dieser Rubrik, die immer am Anfang eines Kapitels steht, möchte ich Ihnen zeigen, wofür ich Python nutze und warum ich es mag. Sie werden vielleicht noch nicht verstehen, was ich genau mache, aber Sie sehen damit schon einmal die Möglichkeiten von Python und können später darauf zurückgreifen. Da dies auch ein Exkurs ist, können Sie diese Rubrik gerne auch erst einmal überspringen.\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# Quellen: https://docs.python.org/3/howto/sockets.html und \n", - "# http://www.binarytides.com/python-socket-server-code-example/\n", + "[Chapter 7: Iteration](http://greenteapress.com/thinkpython2/html/thinkpython2008.html) \n", "\n", "import socket\n", "\n", - "# function for handling connections. This will be used to create threads\n", - "def client_thread(conn):\n", - " # sending message to connected client\n", - " conn.send(bytearray('Welcome to the server. Type something and hit enter\\n', \"utf-8\")) #send only takes string\n", - " \n", - " # infinite loop so that function do not terminate and thread do not end.\n", - " while True:\n", - " \n", - " # receiving from client\n", - " data = conn.recv(1024)\n", - " if not data: \n", - " break\n", - " # print on server side\n", - " print(data.decode(\"utf-8\"))\n", - " # echo to client side\n", - " reply = bytearray('OK ... ', \"utf-8\") + data\n", - " conn.sendall(reply)\n", - " \n", - " # came out of loop\n", - " conn.close()\n", - " \n", - "# create an INET, STREAMing socket\n", - "serversocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)\n", - "# bind the socket to a public host, and a port\n", - "# serversocket.bind((socket.gethostname(), 8080))\n", - "# use localhost instead \n", - "serversocket.bind((\"localhost\", 8080))\n", - "# become a server socket\n", - "serversocket.listen(5)\n", + "Dieses Kapitel handelt von der Iteration - der Möglichkeit, eine Folge von Anweisungen zu wiederholen. Wir haben eine Art der Iteration unter Verwendung der Rekursion schon im [Abschnitt 5.8](seminar05.ipynb#5.8-Rekursion) gesehen und eine andere Art, mit Hilfe der `for`-Schleife, in [Abschnitt 4.2](seminar04.ipynb#4.2-Einfache-Wiederholung). In diesem Kapitel lernen wir eine weitere Variante unter Verwendung der `while`-Anweisung kennen. Aber vorher schauen wir uns noch einmal die Zuweisung eines Wertes an eine Variable an. \n", "\n", + "### Ihre Lernziele:\n", "\n", - "while True:\n", - " # accept connections from outside\n", - " (clientsocket, address) = serversocket.accept()\n", - " # now do something with the clientsocket\n", - " # in this case, we'll pretend this is a threaded server\n", - " ct = client_thread(clientsocket)\n", - " ct.run()\n", + "Beschreiben Sie in 2-3 Stichpunkten kurz was Sie im Seminar heute lernen wollen. Klicken Sie dazu doppelt auf diesen Text und bearbeiten Sie dann den Text:\n", "\n", - "serversocket.close()" + "- \n", + "- \n", + "- " ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "Dieser Code enthält das Grundgerüst, um eine auf TCP basierende Serveranwendung zu programmieren. Wenn Sie den Code starten und sich dann mit Hilfe von Telnet mit der Anwendung verbinden (`telnet localhost 8080`), werden alle Eingaben an Sie zurückgespiegelt (\"echo\") und hier in Jupyter ausgegeben. Das ist eine sehr einfache Testanwendung, die aus Sicherheitsgründen nur von Ihrem Rechner aus erreichbar ist. \n", + "## Exkurs: Was mir an Python gefällt\n", "\n", - "Falls Ihr Rechner keinen Paketfilter (\"Firewall\") laufen hat und Sie die Zeile mit `localhost` auskommentieren und stattdessen die mit `socket.gethostbyname()` aktivieren, dann ist die Anwendung ggf. auch von anderen Rechnern erreichbar und stellt ziemlich sicher eine Sicherheitslücke dar. Gehen Sie also vorsichtig mit dieser Option um. " + "Wir wünschen Ihnen ein frohes Fest und einen guten Rutsch ins neue Jahr." ] }, { - "cell_type": "markdown", + "cell_type": "code", + "execution_count": null, "metadata": {}, + "outputs": [], "source": [ - "## 7 Iteration\n", + "\"\"\" \n", + "Quelle: https://teampython.wordpress.com/2015/12/12/print-a-christmas-tree/\n", + "Python 3 version by antiloquax (2015), based on code from datamungeblog.com.\n", + "\"\"\"\n", + " \n", + "from random import choice\n", + "from random import random\n", + " \n", + "# If you change this, use an odd number.\n", + "size = 21\n", + "\n", + "# Probability that a character will be green.\n", + "prob_gr = 0.6\n", + "# Colour codes.\n", + "colours = [31, 33, 34, 35, 36, 37]\n", + "# Characters to use for decorations. Experiment with these.\n", + "# The chr(169) and chr(174) characters may not work in all terminals\n", + "# (extended ASCII, c and r in a circle).\n", + "decs = ['@', '&', '*', chr(169), chr(174)]\n", + "\n", + "# Format string for printing blinking characters.\n", + "blink_col = \"\\033[5;{0}m{1}\\033[0m\"\n", + "# String to print a green octothorpe ('#').\n", + "leaf = \"\\033[32m#\\033[0m\"\n", + "\n", + "# Width of the tree, will grow by 2 each time.\n", + "width = 1\n", + "# Initialise the tree string, with a star at the top.\n", + "tree = \"\\n{}*\\n\".format(' ' * (size))\n", + "\n", + "\"\"\" Main Loop starts now.\"\"\"\n", + " \n", + "\"\"\" We can't use the normal \"format\" centering approach:\n", + " (\"{:^nn}\".format(string) where \"nn\" is the width of the line), \n", + " with these ansi codes. This is because Python sees the strings as being\n", + " more than one character long (15 & 10 for baubles and leaves).\"\"\"\n", + "\n", + "# Loop from (size - 1) down to 0, using the counter as the padding size.\n", + "for pad in range(size - 1, -1, -1):\n", + " # Increase the width of the tree by 2.\n", + " width += 2\n", + " \n", + " # Put the characters for the line in \"temp\".\n", + " temp = \"\"\n", + " for j in range(width):\n", + " # Make some leaves.\n", + " if random() < prob_gr:\n", + " temp += leaf\n", + " # And also some baubles.\n", + " else:\n", + " temp += blink_col.format(choice(colours), choice(decs))\n", "\n", - "Dieses Kapitel ist eine Übersetzung des [Kapitels 7 \"Iteration\"](http://greenteapress.com/thinkpython2/html/thinkpython2008.html) von Allen B. Downey. \n", + " # Add that string to the line, with padding.\n", + " tree += \"{0}{1}\\n\".format(' ' * pad, temp)\n", "\n", - "Dieses Kapitel handelt von der Iteration - der Möglichkeit, eine Folge von Anweisungen zu wiederholen. Wir haben eine Art der Iteration unter Verwendung der Rekursion schon im [Abschnitt 5.8](seminar05.ipynb#5.8-Rekursion) gesehen und eine andere Art, mit Hilfe der `for`-Schleife, in [Abschnitt 4.2](seminar04.ipynb#4.2-Einfache-Wiederholung). In diesem Kapitel lernen wir eine weitere Variante unter Verwendung der `while`-Anweisung kennen. Aber vorher schauen wir uns noch einmal die Zuweisung eines Wertes an eine Variable an. " + "# Add a \"trunk\" of 2 lines and return.\n", + "print(tree + \"{0}{1}\\n\".format(' ' * (size - 1), \"000\") * 2)\n", + "print(\"\\x46\\x72\\x6f\\x68\\x65\\x20\\x46\\x65\\x73\\x74\\x74\\x61\\x67\\x65\\x21\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "[Und noch viele weitere schöne Beispiele](https://codegolf.stackexchange.com/questions/15860/)" ] }, { @@ -117,9 +138,9 @@ "\n", "An dieser Stelle wollen wir auf eine häufige Ursache für Verwechslungen hinweisen: Da Python das Gleichheitszeichen (`=`) für die Zuweisung verwendet, ist es verlockend, eine Anweisung wie `a = b` wie eine mathematische Aussage der Gleichheit zu interpretieren, das heisst, die Behauptung, dass `a` und `b` gleich seien. Aber diese Interpretation ist falsch! \n", "\n", - "Erstens ist Gleichheit eine symmetrische Beziehung und die Zuweisung ist es nicht. Beispielsweise gilt in der Mathematik: wenn $a=7$, dann ist auch $7=a$. Aber in Python ist die Anweisung `a = 7` erlaubt und `7 = a` ist es nicht. \n", + "Zum Einen ist Gleichheit eine symmetrische Beziehung und die Zuweisung ist es nicht. Beispielsweise gilt in der Mathematik: wenn $a=7$, dann ist auch $7=a$. Aber in Python ist die Anweisung `a = 7` erlaubt und `7 = a` ist es nicht. \n", "\n", - "Außerdem ist in der Mathematik eine Aussage über die Gleichheit entweder wahr oder falsch und gilt durchgängig. Wenn $a = b$ jetzt gilt, dann wird $a$ stets gleich $b$ sein. Aber in Python kann eine Zuweisung zwei Variablen gleich machen, sie müssen aber nicht durchgängig gleich bleiben:\n" + "Zum Anderen ist in der Mathematik eine Aussage über die Gleichheit entweder wahr oder falsch und gilt durchgängig. Wenn $a = b$ jetzt gilt, dann wird $a$ stets gleich $b$ sein. In Python kann eine Zuweisung zwei Variablen gleich machen, sie müssen aber nicht durchgängig gleich bleiben:\n" ] }, { @@ -138,7 +159,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "Die dritte Zeile ändert den Wert von `a` aber dadurch ändert sich nicht der Wert von `b`, so dass die beiden Variablen nicht mehr gleich sind.\n", + "Die dritte Zeile ändert den Wert von `a`, aber dadurch ändert sich nicht der Wert von `b`, so dass die beiden Variablen nicht mehr gleich sind.\n", "\n", "Variablen neue Werte zuzuweisen ist oft nützlich, aber Sie sollten vorsichtig damit umgehen. Wenn sich die Werte von Variablen häufig ändern, ist der Code schwerer zu lesen und zu debuggen. " ] @@ -205,9 +226,9 @@ "\n", "Computer werden häufig zur Automatisierung sich wiederholender Aufgaben genutzt. Identische oder ähnliche Aufgaben zu wiederholen ohne dabei Fehler zu machen, ist etwas was Computer sehr gut können und Menschen eher schlecht. In einem Computerprogramm wird die Wiederholung auch als **Iteration** bezeichnet. \n", "\n", - "Wir haben bereits zwei Funktionen gesehen, `countdown` und `print_t`, die mit Hilfe einer Rekursion eine Wiederholung durchführen. Da Wiederholung sehr häufig benötigt wird, gibt es in Python Sprachkonstrukte die das vereinfachen. Eines ist die `for`-Anweisung, die wir in [Abschnitt 4.2](seminar04.ipynb#4.2-Einfache-Wiederholung) kennengelernt haben. Darauf kommen wir später noch einmal zurück.\n", + "Wir haben bereits zwei Funktionen gesehen, `countdown` und `print_t`, die mit Hilfe einer Rekursion eine Wiederholung durchführen. Da Wiederholung sehr häufig benötigt wird, gibt es in Python Sprachkonstrukte, die das vereinfachen. Eines ist die `for`-Anweisung, die wir in [Abschnitt 4.2](seminar04.ipynb#4.2-Einfache-Wiederholung) kennengelernt haben. Darauf kommen wir später noch einmal zurück.\n", "\n", - "Eine andere Möglichkeit ist die `while`-Anweisung. Dies ist eine Version von `countdown` die eine `while`-Schleife verwendet:" + "Eine andere Möglichkeit ist die `while`-Anweisung. Dies ist eine Version von `countdown`, die eine `while`-Schleife verwendet:" ] }, { @@ -230,17 +251,17 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "Wir können die `while`-Anweisung fast so lesen, als wäre es natürliche Sprache: \"Solange `n` größer als 0 ist, zeige den Wert von `n` und dann dekrementiere `n`. Sobald 0 erreicht ist, gib das Wort `Abheben!` aus.\"\n", + "Wir können die `while`-Anweisung fast so lesen, als wäre es natürliche Sprache: \"Solange `n` größer als 0 ist, zeige den Wert von `n` an und dann dekrementiere `n`. Sobald 0 erreicht ist, gib das Wort `Abheben!` aus.\"\n", "\n", "Der Kontrollfluss der `while`-Schleife etwas formaler ausgedrückt sieht so aus:\n", "\n", "1. Bestimme ob die Bedingung wahr oder falsch ist.\n", "2. Wenn die Bedingung unwahr ist, beende die `while`-Schleife und fahre mit der Ausführung der nächsten Anweisung nach der eingerückten Folge von Anweisungen fort.\n", - "3. Wenn die Bedingung wahr ist, führe die eingerückte Folge von Anweisungen im Schleifenrumpf aus und gehe dann zu Schritt 1.\n", + "3. Wenn die Bedingung wahr ist, führe die eingerückte Folge von Anweisungen im Schleifenrumpf aus und gehe dann zu Schritt 1 zurück.\n", "\n", "Diese Art von Kontrollfluss wird Schleife genannt, weil der dritte Schritt wieder zum ersten Schritt springt und damit den Kreis (Schleife) schließt. (Im Englischen Original passt es besser: *This type of flow is called a loop because the third step loops back around to the top*.)\n", "\n", - "Der Schleifenrumpf sollte den Wert einer oder mehrerer Variablen ändern, so dass die Bedingung irgendwann einmal unwahr wird und die Schleife beendet wird. Ansonsten wiederholt sich die Schleife für immer, was **Endlosschleife** (*infinite loop*) genannt wird.\n", + "Der Schleifenrumpf sollte den Wert einer oder mehrerer Variablen ändern, sodass die Bedingung irgendwann einmal unwahr wird und die Schleife beendet wird. Ansonsten wiederholt sich die Schleife für immer, was **Endlosschleife** (*infinite loop*) genannt wird.\n", "\n", "\n", "\n", @@ -290,7 +311,8 @@ "source": [ "Da `n` manchmal wächst und manchmal schrumpft gibt es keinen offensichtlichen Beweis, dass `n` jemals 1 erreichen wird oder das Programm beendet wird. Für einige bestimmte Werte von `n` können wir zeigen, dass das Programm beendet wird. Wenn beispielsweise der Startwert eine Potenz von 2 ist (2, 4, 8, 16, 32, ...), dann ist `n` bei jedem Schleifendurchlauf eine gerade Zahl (und wird daher halbiert) bis die Schleife den Wert 1 erreicht. Das eben genannte Beispiel endet mit einer solchen Folge, die mir der Zahl 16 beginnt.\n", "\n", - "Die schwierige Frage ist, ob wir beweisen können, dass dieses Programm für *jeden* positiven Wert von `n` beendet wird. Bis jetzt hat es noch niemand geschafft, dies zu beweisen *oder* das Gegenteil zu beweisen (siehe https://de.wikipedia.org/wiki/Collatz-Problem).\n", + "Die schwierige Frage ist, ob wir beweisen können, dass dieses Programm für *jeden* positiven Wert von `n` beendet wird. Bis jetzt hat es noch niemand geschafft, dies zu beweisen. \n", + "Es hat aber auch noch niemand geschafft das Gegenteil zu beweisen. [Collatz-Problem](https://de.wikipedia.org/wiki/Collatz-Problem).\n", "\n", "\n", "\n", @@ -319,9 +341,9 @@ "source": [ "### 7.4 `break`\n", "\n", - "Manchmal wissen wir nicht, dass es Zeit wird eine Schleife zu beenden, bevor wir den Schleifenrumpf nicht schon zur Hälfte ausgeführt haben. In einem solchen Fall können wir die `break`-Anweisung nutzen, um eine Schleife zu verlassen.\n", + "Manchmal wissen wir nicht, dass es Zeit wird eine Schleife zu beenden, bis wir den Schleifenrumpf bereits zur Hälfte ausgeführt haben. In einem solchen Fall können wir die `break`-Anweisung nutzen, um eine Schleife zu verlassen.\n", "\n", - "Nehmen wir beispielsweise an, wir wollen eine Eingabe von der Nutzerin einlesen bis Sie `fertig` eingibt. Dann könnten wir folgendes schreiben:" + "Nehmen wir beispielsweise an, wir wollen eine Eingabe von der Nutzer_in einlesen bis sie `fertig` eingibt. Dann könnten wir folgendes schreiben:" ] }, { @@ -345,7 +367,7 @@ "source": [ "Die Schleifenbedingung ist `True`, was stets wahr ist, daher läuft die Schleife so lange, bis die `break`-Anweisung erreicht wird.\n", "\n", - "Bei jedem Durchlauf wird die Nutzerin aufgefordert, etwas einzugeben. Wenn Sie `fertig` eingibt, dann beendet die `break`-Anweisung die Schleife. Ansonsten gibt das Programm einfach nur aus, was die Nutzerin eingegeben hat und geht zurück zum Anfang der Schleife. Probieren Sie es selbst einmal aus.\n", + "Bei jedem Durchlauf wird die Nutzer_in aufgefordert, etwas einzugeben. Wenn Sie `fertig` eingibt, dann beendet die `break`-Anweisung die Schleife. Ansonsten gibt das Programm einfach nur aus, was die Nutzer_in eingegeben hat und geht zurück zum Anfang der Schleife. Probieren Sie es selbst einmal aus.\n", "\n", "Diese Art eine `while`-Schleife zu nutzen ist üblich, denn wir können die Bedingung überall innerhalb der Schleife prüfen (nicht nur am Anfang) und wir können die Abbruchbedingung positiv formulieren (\"beende die Schleife, wenn folgendes passiert\") statt negativ (\"fahre fort bis folgendes passiert\")." ] @@ -515,7 +537,7 @@ "\n", "Aber wenn Sie \"faul\" waren, haben Sie vielleicht ein paar Tricks gelernt. Beispielsweise kann man das Produkt einer Zahl $n$ mit 9 berechnen, indem man $n-1$ als erste Ziffer des Ergebnisses aufschreibt und dann $10-n$ als zweite Ziffer anhängt. Dieser Trick ist eine allgemeine Lösung, um jede Zahl mit nur einer Ziffer mit 9 zu multiplizieren. Das ist ein Algorithmus!\n", "\n", - "Genauso sind die Verfahren zur schriftlichen Addition (mit Übertrag), Subtraktion und Division Algorithmen. Eine Eigenschaft von Algorithmen ist, dass Sie keine Intelligenz benötigen, um ausgeführt zu werden. Sie sind mechanische Prozesse bei denen jeder Schritt vom vorherigen mittels einfacher und eindeutiger Regeln folgt.\n", + "Genauso sind die Verfahren zur schriftlichen Addition (mit Übertrag), Subtraktion und Division Algorithmen. Eine Eigenschaft von Algorithmen ist, dass Sie keine Intelligenz benötigen, um ausgeführt zu werden. Sie sind mechanische Prozesse bei denen jeder Schritt auf den vorherigen mittels einfacher und eindeutiger Regeln folgt.\n", "\n", "Algorithmen auszuführen ist langweilig aber sie zu entwerfen ist interessant, intellektuell herausfordernd und ein wesentlicher Teil der Informatik.\n", "\n", @@ -530,7 +552,7 @@ "\n", "Sobald Sie größere Programme schreiben werden Sie bemerken, dass Sie mehr Zeit mit Debuggen verbringen. Mehr Programmcode bedeutet halt auch, dass es mehr Möglichkeiten gibt, einen Fehler zu machen und mehr Stellen, an denen sich \"Bugs\" verstecken können.\n", "\n", - "Eine Möglichkeit die Zeit für das Debuggen zu reduzieren ist \"Debugging durch Halbieren\" (binäre Suche). Wenn Ihr Programm beispielsweise 100 Zeilen hat und Sie jede Zeile einzeln prüfen würden, dann bräuchten Sie 100 Schritte zum Debuggen.\n", + "Eine Möglichkeit die Zeit für das Debuggen zu reduzieren ist \"Debugging durch Halbieren\" (denken Sie an die binäre Suche). Wenn Ihr Programm beispielsweise 100 Zeilen hat und Sie jede Zeile einzeln prüfen würden, dann bräuchten Sie 100 Schritte zum Debuggen.\n", "\n", "Stattdessen können Sie versuchen, das Problem zu halbieren. Gehen Sie (ungefähr) zur Hälfte des Programms und suchen Sie dort nach einem Zwischenwert (eine Variable), den Sie überprüfen können. Fügen Sie eine `print`-Anweisung, die den Zwischenwert ausgibt (oder etwas anderes, was einen prüfbare Auswirkung hat), hinzu und starten Sie das Programm.\n", "\n", @@ -551,7 +573,7 @@ "\n", "- Neuzuweisung: \n", "- Aktualisierung:\n", - "- Initialisierung:\n", + "- Initialisierung: Das Erstellen einer Variablen und die damit verbundene erste Zuweisung eines Wertes\n", "- inkrementieren:\n", "- dekrementieren:\n", "- Iteration:\n", @@ -623,6 +645,96 @@ "test_square_root()" ] }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "\n", + "\n", + "([2038](https://xkcd.com/607/), Randall Munroe)\n", + "\n", + "\n", + "\n", + "([Spoiler Alert](https://xkcd.com/109/), Randall Munroe)\n", + "\n", + "\n", + "1. Schreiben Sie den Kopf der Funktion, überlegen Sie welche Argumente der Funktion übergeben müssen.\n", + "2. Kopieren Sie, wie oben bereits erwähnt die Funktion. \n", + "3. Wenn Sie das Notebook aufmerksam gelesen haben, werden Sie sich an einige Verbesserungen erinnern, die wir vornehmen müssen. \n", + "4. Vor allem heißt das x und y mit der `abs()`-Funktion zu vergleichen, also zu schreiben `abs(x-y)<epsilon` wobei Sie einen Wert für epsilon wählen müssen, der klein genug ist. Fügen Sie diese Änderungen in Ihren Code ein.\n", + "5. Wählen Sie einen geeigneten Wert für x in Abhängigkeit von a. Da fast jeder Wert funktioniert, können Sie ihn frei wählen, sie müssen lediglich sicherstellen, dass x ungleich null ist.\n", + "6. Vergessen Sie nicht Ihre Funktion mit Werten zu testen, die Sie überprüfen können. \n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "def mysqrt(a):\n", + " if a==1:\n", + " x=a\n", + " else:\n", + " x=a-1\n", + " \n", + " epsilon=0.00000000001\n", + " while True:\n", + " y=(x+a/x)/2\n", + " if abs(y-x) < epsilon:\n", + " break\n", + " x=y\n", + " return x\n", + " \n", + " \n", + "mysqrt(25)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Wenn die von Ihnen geschriebene Funktion richtig funktiniert, können Sie an der gewünschten Vergleichstabelle arbeiten. Dazu können Sie folgendermaßen ansetzen:\n", + "\n", + "\n", + "1. Der Kopf der Funktion ist bereits gegeben, aber Sie können bereits den Tabellenkopf schreiben. Dafür schreiben Sie eine `print`-Anweisung für jede Zeile des Tabellenkopfs.\n", + "2. Als nächstes müssen Sie entscheiden ob Sie die Tabelle von oben ausgeben wollen, dann muss a die Werte 1 bis 9 annehmen - Sie brauchen eine Schleife - oder ob Sie die Tabelle für ein beliebiges a berechnen wollen, dann müssen Sie User-Input einrichten.\n", + "3. Wir planen für die Schleife, für diesen Fall können Sie eine `While` oder eine `For` Schleife verwenden. \n", + "4. Prüfen Sie zunächst ob beide Funktionen Werte zurückgeben. Dies ist für die Pythonfunktion der Fall, trifft es auch auf Ihre Funktion zu? Wenn nicht ergänzen Sie die `return`-Anweisung an geeigneter Stelle.\n", + "5. Weißen Sie die beiden Funktionen und damit ihre Rückgabewerte neuen Funktionen zu. \n", + "6. Berechnen Sie die Differenz zwischen `mysqrt()` und `math.sqrt()` und speichern Sie diese in einer neuen Variablen\n", + "7. Für ein nachvollziehbares `a` testen Sie jetzt einmal die 3 Ausgaben. \n", + "8. Fügen Sie die `print` Anweisung hinzu, die die einzelnen Tabellenzeilen ausgibt. \n", + "9. Vergessen Sie nicht den Wert für a bei jedem Schleifendurchlauf zu erhöhen. \n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "import math\n", + "def test_square_root():\n", + " a=1.0\n", + " print('a mysqrt(a) math.sqrt(a) diff')\n", + " print('- --------- ------------ ----')\n", + " while a<10:\n", + " E= mysqrt(a)\n", + " M= math.sqrt(a)\n", + " if E<M:\n", + " diff=M-E\n", + " else:\n", + " diff=E-M\n", + " E=str(E)\n", + " M=str(M)\n", + " print(a,E,(18-len(E))*' ',M,(18-len(M))*' ',diff)\n", + " a=a+1\n", + "\n", + " \n", + "test_square_root()" + ] + }, { "cell_type": "markdown", "metadata": {}, @@ -678,6 +790,42 @@ "# Implementieren Sie hier die Funktion eval_loop" ] }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "\n", + "([Spoiler Alert](https://xkcd.com/109/), Randall Munroe)\n", + "\n", + "1. Schreiben Sie den Funktionskopf\n", + "2. Richten Sie die Nutzereingabe ein und weißen Sie diese einer Variablen zu, damit wir den Input weiter verwenden können. \n", + "3. Da der Nutzer mehrfach eine Eingabe machen soll, muss diese Zuweißung innerhalb einer Schleife stattfinden.\n", + "4. Überlegen Sie welche Schleife Sie verwenden müssen, was ist hier Ihre Abbruchbedingung? Ist Sie positiv oder negativ?\n", + "5. Wenn `fertig` eingegeben wird, soll der letzte berechnete Wert zurückgegeben werden, daher muss dieser in einer Variablen temporär gespeichert werden.\n", + "6. Wenn nicht `fertig` eingegeben wird, wird der neue Ausdruck evaluiert und der Wert der zuvor in der temporären Variablen gespeichert war überschrieben. \n", + "7. Vergessen Sie nicht, dass die temporäre Variable initialisiert werden muss, bevor wir sie zum Speichern von Werten verwenden können. \n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "def eval_loop():\n", + " Eval=0\n", + " while True:\n", + " line = input('> ')\n", + " if line == 'fertig':\n", + " return(Eval)\n", + " break\n", + " Eval=eval(line)\n", + " print(Eval)\n", + " print('Fertig!')\n", + " \n", + "eval_loop()" + ] + }, { "cell_type": "markdown", "metadata": {}, @@ -694,9 +842,74 @@ "\n", "(Eventuell ist die Formel [in der Original-Aufgabenstellung](http://greenteapress.com/thinkpython2/html/thinkpython2008.html#hevea_default541) besser zu lesen.)\n", "\n", - "Schreiben Sie eine Funktion `estimate_pi` die diese Formel nutzt, um einen Näherungswert für $\\pi$ zu berechnen und zurückzugeben. Sie sollten eine `while`-Schleife nutzen, um die Terme der Summe zu solange berechnen, bis der letzte Term kleiner ist als `1e-15` (was die Python-Notation für $10^{-15}$ ist). Sie können Ihr Ergebnis prüfen, indem Sie es mit `math.pi` vergleichen.\n", + "Schreiben Sie eine Funktion `estimate_pi` die diese Formel nutzt, um einen Näherungswert für $\\pi$ zu berechnen und zurückzugeben. Sie sollten eine `while`-Schleife nutzen, um die Terme der Summe zu solange berechnen, bis der letzte Term kleiner ist als `1e-15` (was die Python-Notation für $10^{-15}$ ist). Sie können Ihr Ergebnis prüfen, indem Sie es mit `math.pi` vergleichen.\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "\n", + "([Spoiler Alert](https://xkcd.com/109/), Randall Munroe)\n", + "\n", + "1. Auf den ersten Blick sieht diese Formel sehr überwältigend aus. Machen Sie sich keine Sorgen, wir können die Formel in ihre einzelnen Bestandteile aufsplitten und diese einzeln berechnen.\n", + "2. Wie Sie sehen können wird in der Formel zweimal eine Fakultät berechnet. Dafür können Sie die Funktion, die Sie in Seminar 6 geschrieben verwenden. \n", + "3. Berechnen Sie zuerst die Konstante vor dem Summenzeichen und speichern Sie den Wert in einer Variablen. In unserer Lösung wird diesè Variable `faktor` genannt. \n", + "4. Die `while`-Schleife ersetzt das Summenzeichen. Überlegen Sie sich wie Sie die Bedingung formulieren müssen. Die Abbruchbedingung ist `abs(term)<1e-15` \n", + "5. Das Summenzeichen berechnet Werte für k=0 aufwärts (unedlich anstrebend), also muss die Schleife k hochzählen. \n", + "6. Alles was hinter dem Summenzeichen steht wird in der Schleife berechnet.\n", + "7. In jedem Durchgang der Schleife werden Zähler (hier `num`) und Nenner (hier `den`) einzeln berechnet und je einer Variablen zugewiesen\n", + "8. Anschließend wird der Wert des Terms im aktuellen Schleifendurchlauf berechnet, indem die Konstante vor dem Summenzeichen mit dem Bruch hinter dem Summenzeichen multipliziert wird. \n", + "9. Dieser Wert wird in jedem Schleifendurchlauf auf das Gesamtergebnis addiert\n", + "10. Danach wird geprüft ob die Abbruchbedingung erfüllt ist. \n", + "11. Da diese Formel 1/$\\pi$ berechnet, muss 1/ergebnis gerechnet werden um $\\pi$ zu erhalten. \n", + "\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "import math\n", + "\n", + "def fakultaet(n):\n", + " if not isinstance(n, int):\n", + " print('Die Fakultät ist nur für ganze Zahlen definiert.')\n", + " return None\n", + " elif n < 0:\n", + " print('Die Fakultät für negative ganze Zahlen ist nicht definiert.')\n", + " return None\n", + " elif n == 0:\n", + " return 1\n", + " else:\n", + " return n * fakultaet(n-1)\n", + "\n", + "def estimate_pi():\n", + " faktor = 2 * math.sqrt(2) / 9801 \n", + " ergebnis = 0\n", + " k = 0\n", + " while True:\n", + " num = fakultaet(4*k) * (1103 + 26390*k)\n", + " den = fakultaet(k)**4 * 396**(4*k)\n", + " term = faktor * num / den\n", + " ergebnis= ergebnis + term\n", + " if abs(term) < 1e-15:\n", + " break\n", + " k =k + 1\n", + " \n", + " fast_pi= 1/ ergebnis\n", + " return fast_pi\n", "\n", - "Lösung: http://thinkpython2.com/code/pi.py" + "print(estimate_pi())" ] }, { diff --git a/notebooks/seminar08.ipynb b/notebooks/seminar08.ipynb index d618f1116dd55bd4580987c36d976ecfef8c409d..c42aeec32de21dee59a2d7e947573606854c6117 100644 --- a/notebooks/seminar08.ipynb +++ b/notebooks/seminar08.ipynb @@ -4,13 +4,39 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "# Seminar Problemorientierte Programmierung\n", + "# Seminar Problemorientierte Programmierung" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## 8 Zeichenketten\n", + "[Chapter 8: Strings](http://greenteapress.com/thinkpython2/html/thinkpython2009.html)\n", "\n", - "## Exkurs: Was mir an Python gefällt\n", + "Zeichenketten sind anders als ganze Zahlen, Gleitkommazahlen und Boolesche Werte. Eine Zeichenkette ist eine **Folge** (*sequence*), d.h. eine geordnete Menge einzelner Werte. In diesem Kapitel lernen wir, wie wir auf die Zeichen zugreifen können, aus denen eine Zeichenkette besteht und lernen einige der Funktionen kennen, die für Zeichenketten bereitgestellt werden.\n", + "\n", + "\n", + "\n", + "[VIM](https://browserling.smugmug.com/Weekly-Comic-About-Programmers/i-fkJRphx/L), comic.browserling.com\n", + "\n", + "\n", + "### Ihre Lernziele:\n", "\n", - "In dieser Rubrik, die immer am Anfang eines Kapitels steht, möchte ich Ihnen zeigen, wofür ich Python nutze und warum ich es mag. Sie werden vielleicht noch nicht verstehen, was ich genau mache, aber Sie sehen damit schon einmal die Möglichkeiten von Python und können später darauf zurückgreifen. Da dies auch ein Exkurs ist, können Sie diese Rubrik gerne auch erst einmal überspringen.\n", + "Beschreiben Sie in 2-3 Stichpunkten kurz was Sie im Seminar heute lernen wollen. Klicken Sie dazu doppelt auf diesen Text und bearbeiten Sie dann den Text:\n", "\n", - "Text-Statistik ist ganz einfach machbar - mit dem Wissen aus diesem Kapitel können wir z.B. n-Gramme berechnen. Hier ein Beispiel, welches automatisch eine Webseite herunterlädt und die häufigsten 6-Gramme berechnet. " + "- \n", + "- \n", + "- " + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Exkurs: Was mir an Python gefällt\n", + "\n", + "Text-Statistik ist ganz einfach machbar - mit dem Wissen aus diesem Kapitel können wir z.B. n-Gramme berechnen. Vielleicht kommen Sie ja am Ende dieses Notebooks zu diesem Exkurs zurück. Hier ein Beispiel, welches automatisch eine Webseite herunterlädt und die häufigsten 6-Gramme berechnet. " ] }, { @@ -115,7 +141,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "Somit ist `b` der 0. Buchstabe von `banana`, `a` ist der 1. Buchstabe und `n` ist der 2. Buchstabe.\n", + "Somit ist `b` der 0. Buchstabe von `banana`, `a` ist der 1. Buchstabe und `n` der 2.\n", "\n", "Als Index können wir einen Ausdruck verwenden, der Variablen und Operatoren enthält:" ] @@ -143,7 +169,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "Aber der Wert des Index muss eine ganze Zahl sein. Ansonsten erhalten wir eine Fehlermeldung:" + "Der Wert des Index muss eine ganze Zahl sein. Ansonsten erhalten wir eine Fehlermeldung:" ] }, { @@ -165,7 +191,7 @@ "\n", "### 8.2 `len`\n", "\n", - "`len` ist eine eingebaute Funktion, die die Anzahl der Zeichen einer Zeichenkette zurückgibt:\n" + "`len` ist eine eingebaute Funktion, die die Anzahl der Zeichen einer Zeichenkette zurückgibt; sie haben die `len`-Funktion bereits im 3. Seminar kennengelernt und für die erste Hausaufgabe verwendet:\n" ] }, { @@ -286,7 +312,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "Eine andere Möglichkeit einen Durchlauf zu schreiben ist mit Hilfe der `for`-Schleife:" + "Eine einfachere Möglichkeit einen Durchlauf zu schreiben ist mit Hilfe der `for`-Schleife:" ] }, { @@ -329,7 +355,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "Natürlich ist das nicht ganz korrekt, denn \"Ouack\" und \"Quack\" sind flasch geschrieben. Verändern Sie das Programm, um diesen Fehler zu beheben." + "Natürlich ist das nicht ganz korrekt, denn \"Ouack\" und \"Quack\" sind flasch geschrieben. Verändern Sie das Programm, um diesen Fehler zu beheben. *Hinweis: Hier muss eine Entscheidung getroffen und eine von zwei Möglichkeiten verwendet werden.*" ] }, { @@ -448,7 +474,7 @@ "source": [ "Das Objekt (\"object\") in diesem Beispiel ist die Zeichenkette und das Element (\"item\") das Zeichen, welches wir zuweisen wollten. Momentan können wir uns unter einem Objekt das gleiche wie einen Wert vorstellen, aber wir werden später (in [Abschnitt 10.10](seminar10.ipynb#Objekte-und-Werte)) genauer kennenlernen, was Objekte sind.\n", "\n", - "Der Grund für den Fehler ist, dass Zeichenketten **unveränderbar** (*immutable*) sind. Das heisst, wie können eine existierende Zeichenkette nicht verändern. Das beste, was wir machen können, ist eine neue Zeichenkette zu erzeugen, die eine Variante des Originals ist:" + "Der Grund für den Fehler ist, dass Zeichenketten **unveränderbar** (*immutable*) sind. Das heisst, wir können eine existierende Zeichenkette nicht verändern. Das Beste, was wir machen können, ist eine neue Zeichenkette zu erzeugen, die eine Variante des Originals ist:" ] }, { @@ -499,9 +525,9 @@ "source": [ "Auf gewisse Weise ist `find` das Inverse des `[]`-Operators. Anstatt einen Index zu nehmen und das entsprechende Zeichen zu extrahieren nimmt es ein Zeichen und findet den Index, an dem dieses Zeichen auftaucht. Wenn das Zeichen nicht gefunden wird, dann gibt die Funktion `-1` zurück.\n", "\n", - "Das ist das erste Mal, dass wir eine `return`-Anweisung innerhalb einer Schleife sehen! Wenn `word[index] == letter`, dann bricht die Funktion die Schleife ab und gibt direkt `index` zurück.\n", + "Das ist das erste Mal, dass wir eine `return`-Anweisung innerhalb einer Schleife sehen und sie nicht \"nur\" in einer Verzweigung auftritt! Wenn `word[index] == letter` den boolschen Wert `True` zurückliefert, dann bricht die Funktion die Schleife ab und gibt direkt `index` zurück.\n", "\n", - "Wenn das Zeichen `letter` nicht in der Zeichenkette auftaucht, dann wird die Schleife ganz normal verlassen und es wird `-1` zurückgegeben.\n", + "Wenn das Zeichen `letter` nicht in der Zeichenkette auftaucht, wird die Schleife ganz normal verlassen und es wird `-1` zurückgegeben.\n", "\n", "Dieses Berechnungsmuster - eine Folge durchlaufen und zurückkehren, sobald wir gefunden haben, wonach wir suchen - wird **Suche** genannt.\n", "\n", @@ -514,7 +540,7 @@ "metadata": {}, "outputs": [], "source": [ - "# Implementieren Sie hier die veränderte Funktion find" + "# Implementieren Sie hier die veränderte Funktion find\n" ] }, { @@ -548,9 +574,9 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "Das Program demonstriert ein anderes Berechnungsmuster, das **Zähler** (*counter*) genannt wird. Die Variabe `count` wird mit 0 initialisiert und dann jedesmall erhöht, wenn ein `a` gefunden wird. Wenn die Schleife beendet ist, dann enthält `count` das Ergebnis - die Gesamtzahl an `a`s.\n", + "Das Program demonstriert ein anderes Berechnungsmuster, das **Zähler** (*counter*) genannt wird. Die Variabe `count` wird mit 0 initialisiert und dann jedesmall erhöht, wenn ein `a` gefunden wird. Wenn die Schleife beendet ist, dann enthält `count` das Ergebnis - die Gesamtzahl an '`a`'s.\n", "\n", - "Verkapseln Sie den Code in einer Funktion mit Namen `count` und verallgemeinern Sie die Funktion, so dass sie die Zeichenkette und das gesuchte Zeichen als Parameter akzeptiert." + "Verkapseln Sie den Code in einer Funktion mit Namen `count` und verallgemeinern Sie die Funktion so, dass sie die Zeichenkette und das gesuchte Zeichen als Parameter erwartet." ] }, { @@ -593,7 +619,7 @@ "source": [ "### 8.8 Methoden für Zeichenketten\n", "\n", - "Für Zeichenketten stellt Python eine Menge nützlicher Methoden bereit. Eine **Methode** ist ähnlich wie eine Funktion - sie erwartet Argumente und gibt Werte zurück - aber die Syntax ist anders. Beispielsweise erwartet die Methode `upper` eine Zeichenkette und gibt eine neue Zeichenkette zurück, in der alle Buchstaben GROSS geschrieben sind.\n", + "Für Zeichenketten stellt Python eine Menge nützlicher Methoden bereit. Eine **Methode** ist ähnlich wie eine Funktion - sie erwartet Argumente und gibt Werte zurück - aber die Syntax ist anders. Vielleicht können Sie erkennen, dass wir für das `turtle` Modul bereits Methoden verwendet haben. Beispielsweise erwartet die Methode `upper` eine Zeichenkette und gibt eine neue Zeichenkette zurück, in der alle Buchstaben GROSS geschrieben sind.\n", "\n", "Anstatt der Funktions-Syntax `upper(word)` nutzt die Methode jedoch die Syntax `word.upper()`:\n" ] @@ -981,7 +1007,7 @@ "- Objekt:\n", "- Folge:\n", "- Element:\n", - "- Index:\n", + "- Index: Der Index beschreibt die Position in einer Folge, an der eine Aktion ausgeführt wird. Das erste Indexelement ist fast immer `0`\n", "- Segment:\n", "- leere Zeichenkette:\n", "- unveränderbar:\n", @@ -1002,7 +1028,7 @@ "\n", "#### Aufgabe 1\n", "\n", - "Lesen Sie die Dokumentation für die Zeichenketten-Methoden auf der Seite https://docs.python.org/3/library/stdtypes.html#string-methods (sie müssen ggf. herunterscrollen bis zum Abschnitt \"4.7.1. String Methods\"). Probieren Sie einige der Methoden aus, um sich mit ihnen vertraut zu machen. Die Methoden `strip` und `replace` sind besonders nützlich. " + "Lesen Sie die Dokumentation für die Zeichenketten-Methoden auf [dieser Seite]( https://docs.python.org/3/library/stdtypes.html#string-methods), sie müssen ggf. herunterscrollen bis zum Abschnitt \"4.7.1. String Methods\". Probieren Sie einige der Methoden - mindestens 5- aus, um sich mit ihnen vertraut zu machen. Die Methoden `strip` und `replace` sind besonders nützlich. \n" ] }, { @@ -1076,6 +1102,33 @@ "# Implementieren Sie hier die Variante von is_palindrome" ] }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "\n", + "<a data-flickr-embed=\"true\" href=\"https://www.flickr.com/photos/jasoneppink/4964471335\" title=\"Spoiler Alert\"><img src=\"https://farm5.staticflickr.com/4110/4964471335_1f86a923f3_n.jpg\" width=\"320\" height=\"213\" alt=\"Spoiler Alert\"></a><script async src=\"//embedr.flickr.com/assets/client-code.js\" charset=\"utf-8\"></script>\n", + "\n", + "(Quelle: Jason Eppink, Flickr)\n", + "\n", + "\n", + "1. Schreiben Sie wie immer erst den Funktionskopf.\n", + "2. Überlegen Sie was Sie über boolsche Werte wissen, schreiben sie den Vergleich ruhig ersteinmal mehrzeilig auf und überlegen Sie dann wie sie Zeilen zusammenfassen können." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "def is_palindrome(s):\n", + " return s==s[::-1]\n", + "\n", + "\n", + "is_palindrome(\"AnnA\")" + ] + }, { "cell_type": "markdown", "metadata": {}, @@ -1124,18 +1177,37 @@ " return False\n", " return True\n", "\n", + "any_lowercase5 (\"Hallo\")\n", "# Testen Sie hier am besten die Funktionen durch und fügen Sie dann \n", "# oben zu jeder Funktion einen Kommentar hinzu, der erklärt, was die\n", "# jeweilige Funktion wirklich tut." ] }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "\n", + "<a data-flickr-embed=\"true\" href=\"https://www.flickr.com/photos/jasoneppink/4964471335\" title=\"Spoiler Alert\"><img src=\"https://farm5.staticflickr.com/4110/4964471335_1f86a923f3_n.jpg\" width=\"320\" height=\"213\" alt=\"Spoiler Alert\"></a><script async src=\"//embedr.flickr.com/assets/client-code.js\" charset=\"utf-8\"></script>\n", + "\n", + "(Quelle: Jason Eppink, Flickr)\n", + "\n", + "Hier gibt es keine Lösungsansätze, schauen sie sich die Lösungen von unten an und versuchen Sie nachzuvollziehen wie der tatsächliche Effekt entsteht. Fragen Sie dabei auf jeden Fall nach, wenn Sie etwas nicht verstehen!\n", + "\n", + " - lowercase1 prüft ob alle Zeichen Kleinbuchstaben sind.\n", + " - lowercase2 prüft ob `c`ein Kleinbuchstabe ist und gibt immer true -als string, nicht als boolscher Wert- zurück.\n", + " - lowercase3 prüft ob der letzte Buchstabe ein Kleinbuchstabe ist.\n", + " - lowercase4 prüft ob es Kleinbuchstaben gibt, diese Funktion macht das was sie soll.\n", + " - lowercase5 püft ob der erste Buchstabe ein Kleinbuchstabe ist. " + ] + }, { "cell_type": "markdown", "metadata": {}, "source": [ "#### Aufgabe 6\n", "\n", - "Eine [Cäsar-Chiffre](https://de.wikipedia.org/wiki/Caesar-Verschl%C3%BCsselung) ist eine schwache Form der Verschlüsselung, bei der jeder Buchstabe um eine feste Anzahl an Zeichen \"verschoben\" wird. Einen Buchstaben zu verschieben heisst, ihn durch das Alphabet zu schieben, wobei wir, falls notwendig, wieder am Anfang anfangen, so dass 'A' um drei verschoben 'D' ergibt und 'Z' um 1 verschoben 'A' ergibt. \n", + "Eine [Cäsar-Chiffre](https://de.wikipedia.org/wiki/Caesar-Verschl%C3%BCsselung) ist eine schwache Form der Verschlüsselung, bei der jeder Buchstabe um eine feste Anzahl an Zeichen \"verschoben\" wird. Einen Buchstaben zu verschieben heißt, ihn durch das Alphabet zu schieben, wobei wir, falls notwendig, wieder am Anfang anfangen, sodass 'A' um drei verschoben 'D' ergibt und 'Z' um 1 verschoben 'A'. \n", "\n", "\n", "\n", @@ -1188,9 +1260,56 @@ "\n", "\n", "\n", - "([Rot13](https://imgur.com/gallery/T7BD6), vnznfyhg)\n", + "([Rot13](https://imgur.com/gallery/T7BD6), vnznfyhg)\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "\n", + "<a data-flickr-embed=\"true\" href=\"https://www.flickr.com/photos/jasoneppink/4964471335\" title=\"Spoiler Alert\"><img src=\"https://farm5.staticflickr.com/4110/4964471335_1f86a923f3_n.jpg\" width=\"320\" height=\"213\" alt=\"Spoiler Alert\"></a><script async src=\"//embedr.flickr.com/assets/client-code.js\" charset=\"utf-8\"></script>\n", "\n", - "(Musterlösung: http://thinkpython2.com/code/rotate.py)" + "\n", + "1. Schreiben Sie das Grundgerüst für `rotate_word()`, dieses braucht einzugebende Argumente und muss am Ende einen String zurückgeben.\n", + "2. Jeder Buchstabe muss einzeln rotiert werden. Das lagern wir in eine weitere Funktion namens rotate_letters aus, schreiben Sie auch dafür wieder die Grundstruktur, also den Kopf der Funktion und auch hier wieder einen Rückgabewert.\n", + "3. Vor allem Leerzeichen, aber auch alle Zahlen und Sonderzeichen sollen nicht verschoben, sondern einfach zurückgegeben werden. Auch sind Groß- und Kleinschreibung getrennt voneinander codiert. Daher müssen Sie das eingegebene Zeichen prüfen. Sie können dafür die Methoden `isupper()` und `islower()` verwenden. \n", + "3. Zum Prüfen können Sie eine `if`- Anweisung verwenden. Stellen Sie sicher, dass alle Eingabefälle abgedeckt sind.\n", + "4. In dieser Verzweigung legen Sie einen Start- bzw. Vergleichswert fest. Dafür wählen Sie den numerischen Wert von `a` - in Abhängigkeit des eingegebenen Buchstabens als Groß- oder Kleinbuchstabe.\n", + "5. Berechnen Sie die Position im Alphabet indem Sie diesen Startwert von dem Wert des eingegebenen Buchstaben abziehen.\n", + "6. Berechnen Sie den neuen Wert indem Sie `n` hinzuaddieren, mit Rest durch 26 teilen und dann wieder start hinzuaddieren um einen neuen gültigen Wert zu bekommen. Dies ist für die Fälle nötig, bei denen über `z` hinausgegangen wird und wieder am Anfang des Alphabets begonnen werden soll. \n", + "7. Wenn jetzt `rotate_letter` funktioniert, können Sie sich wieder `rotate_word` zuwenden. Hier müssen Sie eine neue Zeichenkette mit den berechneten Buchstaben erzeugen. \n", + "8. Sie fangen mit einer leeren Zeichenkette an. Dann durchlaufen Sie das eingegebene Wort Buchstabe für Buchstabe.\n", + "9. Schreiben Sie dafür eine `for`-Schleife. \n", + "10. In der Schleife rufen Sie `rotate_letter` für den jeweiligen Buchstaben auf und fügen das Ergebnis der in `rotate_word` erzeugten Zeichenkette hinzu.\n", + "11. Nach Verlassen der Schleife wird das neue Wort ausgegeben. " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "def rotate_letter (s,n):\n", + " if s.isupper():\n", + " start= ord('A')\n", + " elif s.islower():\n", + " start=ord('a')\n", + " else:\n", + " return s\n", + " letter=ord(s)-start\n", + " new=(letter+n)%26+start\n", + " return chr(new)\n", + "\n", + "\n", + "def rotate_word(word,n):\n", + " neu=''\n", + " for letter in word:\n", + " neu=neu+rotate_letter(letter,n)\n", + " return neu\n", + " \n", + "rotate_word(\"cheer\",7)" ] }, {