diff --git a/notebooks/seminar06.ipynb b/notebooks/seminar06.ipynb index e9982803f4d90c89734a1a8feb8b777fbb1f672b..8b6174df8f7a05daaf52aee0f00f3fc4668cbd46 100644 --- a/notebooks/seminar06.ipynb +++ b/notebooks/seminar06.ipynb @@ -6,9 +6,31 @@ "source": [ "# Seminar Problemorientierte Programmierung\n", "\n", + "## 6 Ertragreiche Funktionen\n", + "[Chapter 6: Fruitful function](http://greenteapress.com/thinkpython2/html/thinkpython2007.html)\n", + "\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 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": [ "## Exkurs: Was mir an Python gefällt\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" + "Dieser Code enthält das Grundgerüst, um eine auf TCP basierende Serveranwendung zu programmieren." ] }, { @@ -17,84 +39,60 @@ "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", + "# Quellen: https://docs.python.org/3/howto/sockets.html und \n", + "# http://www.binarytides.com/python-socket-server-code-example/\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", - " # 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", + " # 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", "\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\")" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Und noch viele weitere schöne Beispiele: https://codegolf.stackexchange.com/questions/15860/" + "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", + "\n", + "serversocket.close()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "## 6 Ertragreiche Funktionen\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", - "\n", - "\n", + "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", "\n", - "([Random Number](https://xkcd.com/221/), Randall Munroe)" + "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." ] }, { @@ -171,7 +169,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 +193,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 +226,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 +244,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 +252,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 +297,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 +320,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 +345,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 +373,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 +604,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 +697,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 +710,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 +854,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 +877,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 +898,8 @@ " rekursion = fakultaet(n-1)\n", " ergebnis = n * rekursion\n", " print(space, 'returning', ergebnis)\n", - " return ergebnis" + " return ergebnis\n", + " " ] }, { @@ -940,7 +941,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 +999,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 +1015,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 +1090,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", + "<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", - "Lösung: http://thinkpython2.com/code/palindrome_soln.py" + "(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 +1137,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 +1169,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 +1227,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": {},