{ "cells": [ { "cell_type": "markdown", "id": "8aff1e18-bc94-401c-a6b6-794c36ac2c75", "metadata": {}, "source": [ "# Kapitel 3: Funktionen\n", "[Chapter 3: Functions](https://colab.research.google.com/github/AllenDowney/ThinkPython/blob/v3/chapters/chap03.ipynb)\n", "\n", "Im vorherigen Kapitel haben wir sowohl einige der **Funktionen** verwendet, die Python zur Verfügung stellt, darunter `int`und `float`, als auch einige **Funktionen** des `math`-Moduls, wie `sqrt`und `pow`.\n", "In diesem Kapitel werden wir lernen, eigene Funktionen zu erstellen und diese auszuführen.\n", "Außerdem werden wir sehen, wie eine Funktion eine andere aufrufen kann.\n", "Als Beispiele werden wir Songtexte aus Monty Python-Liedern darstellen.\n", "Diese etwas albernen Beispiele werden ein wichtiges Merkmal aufzeigen -- die Fähigkeit, eigene Funktionen zu schreiben, ist die Grundlage des Programmierens.\n", "\n", "In diesem Kapitel wird mit der `for`-Schleife zudem ein neue Anweisung eingeführt, die verwendet wird, um Berechnungen zu wiederholen.\n", "\n", "**Bevor Sie mit diesem Notebook starten, sollten Sie wiederholen, was Sie im letzten Notebook gelernt haben. Gehen Sie zurück und schauen Sie sich mindestens das Glossar an und wiederholen Sie die dort genannten Begriffe.**" ] }, { "cell_type": "markdown", "id": "467d43be-8687-40ae-a5cf-159a0b4811f3", "metadata": {}, "source": [ "## Ihre Lernziele\n", "Sie können eine Übersicht der Inhalte dieses Notebooks einblenden mit *Strg + Shift + k*.\n", "\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", "\n", "## Exkurs: Was mir an Python gefällt\n", "\n", "Es ist sehr leicht, Ergebnisse mit Hilfe eines Plots darzustellen und auch mathematische Funktionen können professionell geplottet werden:" ] }, { "cell_type": "code", "execution_count": null, "id": "b4e1ec70-4905-4336-853f-bb4fde52b24e", "metadata": {}, "outputs": [], "source": [ "%matplotlib inline\n", "import math\n", "import matplotlib.pyplot as plt\n", "\n", "# sin(x) und cos(x) zwischen 0 und 2pi\n", "x = [xx/10 for xx in range(0,int(2*math.pi*10))]\n", "sinx = [math.sin(xx) for xx in x]\n", "cosx = [math.cos(xx) for xx in x]\n", "plt.plot(x, sinx, label='sin(x)')\n", "plt.plot(x, cosx, label='cos(x)')\n", "plt.grid(linestyle=\"dashed\")\n", "plt.legend()\n", "plt.show()\n" ] }, { "cell_type": "markdown", "id": "74e50814-9a6d-454b-90bb-b581324905a6", "metadata": {}, "source": [ "## Herunterladen des unterstützenden Codes\n", "Die folgende Zelle lädt eine Datei herunter und führt einen Code aus, der speziell für dieses Notebook verwendet wird. Sie müssen diesen Code nicht verstehen, aber Sie sollten die Zelle ausführen vor allen weiteren Zellen in diesem Notebook." ] }, { "cell_type": "code", "execution_count": null, "id": "696d0972-db96-42cd-8b7a-11b0bd5ec98d", "metadata": { "id": "103cbe3c", "tags": [] }, "outputs": [], "source": [ "from os.path import basename, exists\n", "\n", "def download(url):\n", " filename = basename(url)\n", " if not exists(filename):\n", " from urllib.request import urlretrieve\n", "\n", " local, _ = urlretrieve(url, filename)\n", " print(\"Downloaded \" + str(local))\n", " return filename\n", "\n", "download('https://github.com/AllenDowney/ThinkPython/raw/v3/thinkpython.py');\n", "download('https://github.com/AllenDowney/ThinkPython/raw/v3/diagram.py');\n", "\n", "import thinkpython" ] }, { "cell_type": "markdown", "id": "02695f5a-bb98-40c9-90c9-8bce99625886", "metadata": {}, "source": [ "## Neue Funktionen hinzufügen" ] }, { "cell_type": "code", "execution_count": null, "id": "36b4c06a-ca85-4793-8290-92e539487c84", "metadata": {}, "outputs": [], "source": [ "from IPython.lib.display import YouTubeVideo\n", "YouTubeVideo('https://www.youtube.com/watch?v=LQCfN5HS9xI&list=PL_pqkvxZ6ho3u8PJAsUU-rOAQ74D0TqZB&index=18')" ] }, { "cell_type": "markdown", "id": "b4ea99c5", "metadata": { "id": "b4ea99c5" }, "source": [ "Eine **Funktionsdefinition** gibt den Namen der Funktion an und beschreibt die Folge von Anweisungen, die ausgeführt werden, wenn die Funktion aufgerufen wird:" ] }, { "cell_type": "code", "execution_count": null, "id": "d28f5c1a", "metadata": { "id": "d28f5c1a" }, "outputs": [], "source": [ "def print_lyrics():\n", " print(\"Jetzt fahr'n wir über'n See, über'n See,\")\n", " print(\" jetzt fahr'n wir über'n See\")" ] }, { "cell_type": "markdown", "id": "e23fa85a-9cdb-43f8-8a63-a41f63390df6", "metadata": {}, "source": [ "- Dabei ist `def` ein Schlüsselwort, welches dem Python-Interpreter verrät, dass jetzt eine Funktionsdefinition folgt,\n", "- `print_lyrics` ist der Name der Funktion (es gelten die gleichen Regeln wie für Variablennamen: nur Buchstaben, Zahlen und Unterstrich und keine Zahlen am Anfang des Namens),\n", "- Die leeren Klammern am Ende zeigen an, dass diese Funktion keine Argumente erwartet.\n", "- Die erste Zeile einer Funktion ist der **Kopf** (Englisch: *header*), welcher mit einem Doppelpunkt abgeschlossen wird.\n", "- Danach kommt der **Rumpf** (Englisch: *body*), der um vier Leerzeichen eingerückt werden muss.\n", "- Im Rumpf können beliebig viele Anweisungen stehen; die erste nicht mehr eingerückte Anweisung gehört nicht mehr zur Funktion.\n", "\n", "Durch die Definition einer Funktion wird ein sogenanntes **Funktionsobjekt** erzeugt, das wir uns auf die folgende Weise anzeigen lassen können:" ] }, { "cell_type": "code", "execution_count": null, "id": "2850a402", "metadata": { "id": "2850a402" }, "outputs": [], "source": [ "print_lyrics" ] }, { "cell_type": "markdown", "id": "12bd0879", "metadata": { "id": "12bd0879" }, "source": [ "Die Ausgabe weist darauf hin, dass die Funktion `print_lyrics` kein Argument aufnimmt.\n", "`__main__` ist der Name des Moduls, das `print_lyrics` enthält.\n", "\n", "Jetzt wo wir eine Funktion definiert haben, können wir sie auf die selbe Art wie eingebaute Funktionen aufrufen:" ] }, { "cell_type": "code", "execution_count": null, "id": "9a048657", "metadata": { "id": "9a048657" }, "outputs": [], "source": [ "print_lyrics()" ] }, { "cell_type": "markdown", "id": "43a67612-2672-483d-be0f-94bb141d9d74", "metadata": {}, "source": [ "Dazu müssen wir - analog zum Aufruf von vordefinierten Funktionen - den Namen der Funktion **gefolgt von den Klammern** schreiben. Die Klammern sind leer, da die Funktion keine Argumente erwartet. \n", "\n", "Wenn die Funktion läuft, führt sie die Anweisungen im Rumpf aus, hier bildet sie daher die ersten zwei Zeilen von \"Jetzt fahr'n wir über'n See\" ab." ] }, { "cell_type": "markdown", "id": "cac10e0a-9fbf-44b6-aa93-3b9bb7eba399", "metadata": {}, "source": [ "## Parameter\n", "\n" ] }, { "cell_type": "code", "execution_count": null, "id": "af32602a-ceee-49b4-9811-70f0eb20e256", "metadata": {}, "outputs": [], "source": [ "from IPython.lib.display import YouTubeVideo\n", "YouTubeVideo('https://www.youtube.com/watch?v=af9ORp1Pty0&list=PL_pqkvxZ6ho3u8PJAsUU-rOAQ74D0TqZB&index=19')" ] }, { "cell_type": "markdown", "id": "36705e87-2bd4-4196-aea5-3794eea57676", "metadata": {}, "source": [ "Einige der Funktionen, die wir schon gesehen haben, benötigen **Argumente** (z.B. `print` oder `type`). Wenn wir `math.sin` aufrufen, müssen wir eine Zahl als Argument übergeben. Einige Funktionen benötigen auch mehrere Argumente (z.B. benötigt `math.pow` zwei Argumente: die Basis und den Exponenten).\n", "\n", "Innerhalb einer Funktion werden die Argumente Variablen zugewiesen. Diese speziellen Variablen werden **Parameter** genannt. Schauen wir uns eine Funktion an, die ein Argument erwartet:" ] }, { "cell_type": "code", "execution_count": null, "id": "884bcdb2-16df-43c7-b7db-16d915f2c79c", "metadata": {}, "outputs": [], "source": [ "def print_twice(wert):\n", " print(wert)\n", " print(wert)" ] }, { "cell_type": "markdown", "id": "1716e3dc", "metadata": { "id": "1716e3dc" }, "source": [ "Der Variablenname in Klammern ist ein **Parameter**.\n", "Wenn die Funktion aufgerufen wird, wird dem Parameter der Wert des Arguments zugewiesen.\n", "Wir können `print_twice`zum Beispiel so aufrufen:" ] }, { "cell_type": "code", "execution_count": null, "id": "a3ad5f46", "metadata": { "id": "a3ad5f46" }, "outputs": [], "source": [ "print_twice('See, ')" ] }, { "cell_type": "markdown", "id": "f02be6d2", "metadata": { "id": "f02be6d2" }, "source": [ "Diese Funktion auszuführen hat die gleiche Wirkung wie dem Parameter das Argument zuzuweisen und anschließend den Rumpf der Funktion auszuführen:" ] }, { "cell_type": "code", "execution_count": null, "id": "042dfec1", "metadata": { "id": "042dfec1" }, "outputs": [], "source": [ "string = 'See, '\n", "print(string)\n", "print(string)" ] }, { "cell_type": "markdown", "id": "ea8b8b6e", "metadata": { "id": "ea8b8b6e" }, "source": [ "Wir können auch eine Variable als Argument verwenden:" ] }, { "cell_type": "code", "execution_count": null, "id": "83837a53-e89b-40ff-ae59-7c603443a29c", "metadata": {}, "outputs": [], "source": [ "text = 'Gähn'\n", "print_twice(text)" ] }, { "cell_type": "markdown", "id": "5c1884ad", "metadata": { "id": "5c1884ad" }, "source": [ "In diesem Beispiel wird dem Parameter `string` der Wert `text` zugewiesen." ] }, { "cell_type": "markdown", "id": "a3e5a790", "metadata": { "id": "a3e5a790" }, "source": [ "## Funktionsaufruf\n", "\n", "Sobald wir eine Funktion definiert haben, können wir diese in einer anderen Funktion benutzen.\n", "\n", "Um das zu demonstrieren, werden wir Funktionen schreiben, die den Text des Liedes \"The Spam Song\" ausgeben (<https://www.songfacts.com/lyrics/monty-python/the-spam-song>).\n", "\n", "> Spam, Spam, Spam, Spam, \n", "> Spam, Spam, Spam, Spam, \n", "> Spam, Spam, \n", "> (Lovely Spam, Wonderful Spam!) \n", "> Spam, Spam,\n", "\n", "Wir beginnen mit der folgenden Funktion, die zwei Parameter aufnimmt:" ] }, { "cell_type": "code", "execution_count": null, "id": "e86bb32c", "metadata": { "id": "e86bb32c" }, "outputs": [], "source": [ "def repeat(word, n):\n", " print(word * n)" ] }, { "cell_type": "markdown", "id": "bdd4daa4", "metadata": { "id": "bdd4daa4" }, "source": [ "Wir können diese Funktion verwenden, um die erste Zeile des Songs auszugeben:" ] }, { "cell_type": "code", "execution_count": null, "id": "ec117999", "metadata": { "id": "ec117999" }, "outputs": [], "source": [ "spam = 'Spam, '\n", "repeat(spam, 4)" ] }, { "cell_type": "markdown", "id": "c6f81e09", "metadata": { "id": "c6f81e09" }, "source": [ "Um die ersten zwei Zeilen anzuzeigen, können wir eine neue Funktion definieren, die `repeat` verwendet:" ] }, { "cell_type": "code", "execution_count": null, "id": "3731ffd8", "metadata": { "id": "3731ffd8" }, "outputs": [], "source": [ "def first_two_lines():\n", " repeat(spam, 4)\n", " repeat(spam, 4)" ] }, { "cell_type": "markdown", "id": "8058ffe4", "metadata": { "id": "8058ffe4" }, "source": [ "Danach können wir diese so aufrufen:" ] }, { "cell_type": "code", "execution_count": null, "id": "6792e63b", "metadata": { "id": "6792e63b" }, "outputs": [], "source": [ "first_two_lines()" ] }, { "cell_type": "markdown", "id": "07ca432a", "metadata": { "id": "07ca432a" }, "source": [ "Um die letzten drei Zeilen darzustellen, können wir eine weitere Funktion definieren, die ebenfalls `repeat` verwendet:" ] }, { "cell_type": "code", "execution_count": null, "id": "2dcb020a", "metadata": { "id": "2dcb020a" }, "outputs": [], "source": [ "def last_three_lines():\n", " repeat(spam, 2)\n", " print('(Lovely Spam, Wonderful Spam!)')\n", " repeat(spam, 2)" ] }, { "cell_type": "code", "execution_count": null, "id": "9ff8c60e", "metadata": { "id": "9ff8c60e" }, "outputs": [], "source": [ "last_three_lines()" ] }, { "cell_type": "markdown", "id": "d6456a19", "metadata": { "id": "d6456a19" }, "source": [ "Schließlich können wir jetzt alles zu einer Funktion zusammenfügen, die die gesamte Strophe ausgibt:" ] }, { "cell_type": "code", "execution_count": null, "id": "78bf3a7b", "metadata": { "id": "78bf3a7b" }, "outputs": [], "source": [ "def print_verse():\n", " first_two_lines()\n", " last_three_lines()" ] }, { "cell_type": "code", "execution_count": null, "id": "ba5da431", "metadata": { "id": "ba5da431" }, "outputs": [], "source": [ "print_verse()" ] }, { "cell_type": "markdown", "id": "d088fe68", "metadata": { "id": "d088fe68" }, "source": [ "Wenn wir `print_verse`ausführen, ruft diese Funktion die Funktion `first_two_lines` auf, die wiederum `repeat` aufruft, was anschließend `print` aufruft.\n", "Das sind eine Menge Funktionen.\n", "\n", "Natürlich hätten wir das gleiche Ziel auch mit weniger Funktionen erreichen können, aber der Sinn dieses Beispiels ist es, zu zeigen auf welche Weise Funktionen zusammenarbeiten können." ] }, { "cell_type": "markdown", "id": "c3b16e3f", "metadata": { "id": "c3b16e3f" }, "source": [ "## Wiederholung\n", "\n", "Wenn wir mehr als eine Strophe darstellen wollen, können wir eine `for`-Anweisung nutzen.\n", "Hier ist ein einfaches Beispiel dafür:" ] }, { "cell_type": "code", "execution_count": null, "id": "29b7eff3", "metadata": { "id": "29b7eff3" }, "outputs": [], "source": [ "for i in range(2):\n", " print(i)" ] }, { "cell_type": "markdown", "id": "bf320549", "metadata": { "id": "bf320549" }, "source": [ "Die erste Zeile ist ein Kopf, der mit einem Doppelpunkt endet.\n", "Die zweite Zeile ist der Rumpf, der eingerückt werden muss.\n", "\n", "Der Kopf beginnt mit dem _Schlüsselwort_ `for`, einer _neuen Variable_ namens `i`und einem _weiteren Schlüsselwort_ `in`.\n", "Er verwendet die `range`-Funktion, um eine Abfolge zweier Werte, `0` und `1`, zu erschaffen.\n", "Wenn wir in Python zählen, beginnen wir normalerweise bei `0`.\n", "\n", "Wenn die `for`-Anweisung läuft, weist sie `i` den ersten Wert von `range` zu und lässt dann die `print`-Funktion im Rumpf ablaufen, die eine `0` ausgibt.\n", "\n", "Wenn das Ende des Rumpfes erreicht wird, wird wie bei einer Schleife zum Kopf zurückgekehrt, weshalb man diese Anweisung auch als **Schleife** bezeichnet.\n", "Beim zweiten Durchlauf der Schleife wird `i` der nächste Wert von `range` zugewiesen und ausgegeben. Danach endet die Schleife, weil das der letzte Wert von `range` war.\n", "\n", "So können wir eine `for`-Schleife verwenden, um zwei Strophen des Liedes auszugeben:" ] }, { "cell_type": "code", "execution_count": null, "id": "038ad592", "metadata": { "id": "038ad592" }, "outputs": [], "source": [ "for i in range(2):\n", " print(\"Verse\", i)\n", " print_verse()\n", " print()" ] }, { "cell_type": "markdown", "id": "88a46733", "metadata": { "id": "88a46733" }, "source": [ "Wir können auch eine `for`-Schleife innerhalb einer Funktion verwenden.\n", "Zum Beispiel nimmt `print_n_verses` einen Parameter `n`, der eine ganze Zahl sein muss, auf und gibt die angegebene Menge an Strophen aus:" ] }, { "cell_type": "code", "execution_count": null, "id": "8887637a", "metadata": { "id": "8887637a" }, "outputs": [], "source": [ "def print_n_verses(n):\n", " for i in range(n):\n", " print_verse()\n", " print()" ] }, { "cell_type": "markdown", "id": "ad8060fe", "metadata": { "id": "ad8060fe" }, "source": [ "In diesem Beispiel verwenden wir `i` zwar nicht im Rumpf der Schleife, allerdings muss im Kopf trotzdem ein Variablenname verwendet werden." ] }, { "cell_type": "markdown", "id": "d90e871a-f9d0-4302-bb56-99cb676658c2", "metadata": {}, "source": [ "## Variablen und Parameter sind lokal\n", "\n", "Wenn wir eine Variable innerhalb einer Funktion erzeugen, ist dies eine **lokale Variable**; das heißt diese Variable existiert nur innerhalb der Funktion. Zum Beispiel:" ] }, { "cell_type": "code", "execution_count": null, "id": "7289b4e7-4237-43df-8d78-4630d3f5f800", "metadata": {}, "outputs": [], "source": [ "def cat_twice(part1, part2):\n", " cat = part1 + part2\n", " print_twice(cat)" ] }, { "cell_type": "markdown", "id": "3d6f33f2-720c-42c3-aa73-d76c2cf4191e", "metadata": {}, "source": [ "Diese Funktion erwartet zwei Argumente, verkettet sie und gibt das Ergebnis zweimal aus. Testen wir das einmal:\n" ] }, { "cell_type": "code", "execution_count": null, "id": "fc8c5479-8b73-4ccc-b6e3-c0bd905e4841", "metadata": {}, "outputs": [], "source": [ "teil1 = 'Holleri du dödel di '\n", "teil2 = 'diri diri dudel dö'\n", "cat_twice(teil1, teil2)" ] }, { "cell_type": "markdown", "id": "69337c00-1674-44c7-ab21-87bfcb0e3dfa", "metadata": {}, "source": [ "Nachdem `cat_twice` ausgeführt wurde, wird die lokale Variable `cat` zerstört. Wenn wir versuchen darauf zuzugreifen, erhalten wir eine Fehlermeldung:" ] }, { "cell_type": "code", "execution_count": null, "id": "87624529-d437-4eec-8a43-22818121d24c", "metadata": {}, "outputs": [], "source": [ "%%expect NameError\n", "\n", "print(cat)" ] }, { "cell_type": "markdown", "id": "674a246c-d00a-4bad-b243-3c3bef09ed27", "metadata": {}, "source": [ "Parameter sind ebenfalls lokal. Außerhalb von `cat_twice` können wir auf `part1` nicht zugreifen:" ] }, { "cell_type": "code", "execution_count": null, "id": "2d6b08ce-d61c-43ee-8d15-4c17711b654a", "metadata": {}, "outputs": [], "source": [ "%%expect NameError\n", "\n", "print(part1)" ] }, { "cell_type": "markdown", "id": "eabac8a6", "metadata": { "id": "eabac8a6" }, "source": [ "## Stapel-Diagramme\n", "\n", "Um den Überblick zu behalten, wo welche Variable genutzt werden kann, ist es manchmal hilfreich, ein sogenanntes **Stapel-Diagramm** (Englisch: *stack diagram*) zu zeichnen.\n", "\n", "Das Diagramm zeigt uns den Wert jeder Variablen und auch die Funktion zu der jede Variable gehört.\n", "\n", "Jede Funktion wird durch einen **Block** (Englisch: *frame*) repräsentiert: links neben dem Block erscheint der _Name der Funktion_ und innerhalb des Blocks die _Parameter_, die _Variablen_ und die _Werte_, die ihnen zugewiesen wurden.\n", "\n", "Hier ist das Stapel-Diagramm für das vorige Beispiel:" ] }, { "cell_type": "code", "execution_count": null, "id": "83df4e32", "metadata": { "id": "83df4e32", "tags": [] }, "outputs": [], "source": [ "from diagram import make_frame, Stack\n", "\n", "d1 = dict(teil1=teil1, teil2=teil2)\n", "frame1 = make_frame(d1, name='__main__', dy=-0.3, loc='left')\n", "\n", "d2 = dict(part1=teil1, part2=teil2, cat=teil1+teil2)\n", "frame2 = make_frame(d2, name='cat_twice', dy=-0.3,\n", " offsetx=0.03, loc='left')\n", "\n", "d3 = dict(s = teil1+teil2)\n", "frame3 = make_frame(d3, name='print_twice',\n", " offsetx=-0.28, offsety=-0.3, loc='left')\n", "\n", "d4 = {\"?\": teil1+teil2}\n", "frame4 = make_frame(d4, name='print',\n", " offsetx=-0.28, offsety=0, loc='left')\n", "\n", "stack = Stack([frame1, frame2, frame3, frame4], dy=-0.8)" ] }, { "cell_type": "code", "execution_count": null, "id": "bcd5e1df", "metadata": { "id": "bcd5e1df", "tags": [] }, "outputs": [], "source": [ "from diagram import diagram, adjust\n", "\n", "\n", "width, height, x, y = [3.8, 2.91, 1.15, 2.66]\n", "ax = diagram(width, height)\n", "bbox = stack.draw(ax, x, y)\n", "#adjust(x, y, bbox)" ] }, { "cell_type": "markdown", "id": "854fee12", "metadata": { "id": "854fee12" }, "source": [ "Die Blöcke sind in einem Stapel (*stack*) angeordnet, der uns zeigt, welche Funktion welche andere Funktion aufgerufen hat. In unserem Beispiel wurde `print` durch `print_twice` aufgerufen, `print_twice` wurde durch `cat_twice` aufgerufen und `cat_twice` wurde durch `__main__` aufgerufen, was ein spezieller Name für den obersten Block ist. Wenn wir eine Variable außerhalb einer Funktion erzeugen, gehört diese zu `__main__`.\n", "\n", "Im Block für `print` weist das Fragezeichen darauf hin, dass wir den Namen des Parameters nicht kennen.\n", "Wenn Sie neugierig sind, können Sie einen virtuellen Assistenten fragen: \"Was sind die Parameter der print-Funktion in Python?\"" ] }, { "cell_type": "markdown", "id": "5690cfc0", "metadata": { "id": "5690cfc0" }, "source": [ "## Tracebacks\n", "\n", "Wenn bei einer Funktion ein Laufzeitfehler vorkommt, zeigt Python den _Namen der laufenden Funktion_, sowie den _Namen der Funktion, die diese aufgerufen hat_ an und arbeitet sich auf diese Weise _weiter durch den Stapel_.\n", "Für ein Beispiel werde ich eine Version von `print_twice` definieren, die einen Fehler enthält -- sie versucht `cat`auszugeben, was eine lokale Variable in einer anderen Funktion ist:" ] }, { "cell_type": "code", "execution_count": null, "id": "886519cf", "metadata": { "id": "886519cf" }, "outputs": [], "source": [ "def print_twice(string):\n", " print(cat) # NameError\n", " print(cat)" ] }, { "cell_type": "markdown", "id": "d7c0713b", "metadata": { "id": "d7c0713b" }, "source": [ "Folgendes passiert, wenn wir `cat_twice` laufen lassen:" ] }, { "cell_type": "code", "execution_count": null, "id": "1fe8ee82", "metadata": { "id": "1fe8ee82", "tags": [] }, "outputs": [], "source": [ "# Diese Zelle weist Jupyter ab, detallierte Debugging-Informationen, darunter\n", "# ein Traceback, auszugeben, wenn ein Laufzeitfehler passiert.\n", "\n", "%xmode Verbose" ] }, { "cell_type": "code", "execution_count": null, "id": "d9082f88", "metadata": { "id": "d9082f88", "tags": [] }, "outputs": [], "source": [ "%%expect NameError\n", "\n", "cat_twice(line1, line2)" ] }, { "cell_type": "markdown", "id": "2f4defcf", "metadata": { "id": "2f4defcf" }, "source": [ "Die Fehlermeldung enthält ein **Traceback**, dieses zeigt den _Namen der laufenden Funktion_, den _Namen der Funktion, die diese aufgerufen hat_ und so weiter an, immer weiter hoch durch das Stapel-Diagramm.\n", "In diesem Beispiel zeigt es, dass `cat_twice` `print_twice` aufgerufen hat, und der Fehler in `print_twice` passiert ist.\n", "\n", "Die Reihenfolge der Funktionen im Traceback ist die gleiche wie die Reihenfolge der Blöcke im Stapel-Diagramm.\n", "Die laufende Funktion befindet sich ganz unten." ] }, { "cell_type": "markdown", "id": "26fa644c-4662-4eac-8325-ce0ebba9ff94", "metadata": {}, "source": [ " \n", "## Wozu Funktionen?\n", "\n", "Warum ist es sinnvoll, ein Programm in Funktionen aufzuteilen?\n", "Hier einige Beispiele: \n", "\n", "- **Lesbarkeit** - Mit einer Funktion können wir eine Gruppe von Anweisungen benennen und so beschreiben was die Gruppe macht.\n", "- **Fehlersuche** - Lange Programme in Funktionen aufzuteilen ermöglicht es, das Programm funktionsweise zu debuggen und später zusammenzusetzen.\n", "- **Testbarkeit** - Analog zur Fehlersuche kann jede Funktion einzeln getestet werden.\n", "- **kürzerer Programmcode** - Durch Funktionen können Wiederholungen im Quellcode reduziert oder eliminiert werden.\n", "- **Wartbarkeit** - Dadurch, dass sich Quellcode nicht (bzw. kaum) wiederholt, können Änderungen an nur einer Stelle vorgenommen werden und die Effekte sind im gesamten Programm zu sehen.\n", "- **Wiederverwendbarkeit** - Eine gut funktionierende Funktion kann in vielen Programmen sinnvoll verwendet werden." ] }, { "cell_type": "markdown", "id": "4d6b8cb5-0395-4750-8435-efec52de2383", "metadata": {}, "source": [ "## Debugging\n", "\n", "Eine der wichtigsten Fähigkeiten des Programmierens ist **Debugging**. Obwohl es manchmal frustrierend ist, ist es einer der intellektuell anspruchsvollsten, herausforderndsten und interessantesten Aspekte des Programmierens.\n", "\n", "Debugging ist wie **Detektiv spielen**: Wir haben ein paar Hinweise und wir müssen die Vorgänge und Ereignisse herausfinden, die zu den Ergebnissen geführt haben, die wir sehen.\n", "\n", "\n", "\n", "Debugging ist aber auch wie eine **experimentelle Wissenschaft**: Wenn wir erstmal eine Idee haben, was schiefgelaufen sein könnte, können wir unser Programm verändern und es noch einmal versuchen. Wenn unsere Annahme richtig war, können wir das Ergebnis unserer Veränderung vorhersagen. Damit kommen wir hoffentlich einem richtig funktionierendem Programm näher. Wenn unsere Annahme falsch war, müssen wir uns eine neue überlegen. Wie schon Sherlock Holmes sagte: _\"Wenn man das Unmögliche ausgeschlossen hat, muss das, was übrig bleibt — wie unwahrscheinlich auch immer —, die Wahrheit sein\"_ (A. Conan Doyle, *The Sign of Four*).\n", "\n", "Für einige Menschen ist Programmieren und Debugging das gleiche. Das heißt, Programmieren bedeutet, dass man ein Programm solange debuggt bis es das tut, was man möchte. Die Idee ist, dass man mit einem kleinen, einfachen - und vor allem funktionierenden- Programm beginnt, und so lange kleine Änderungen vornimmt, bis es tut was es soll. Währenddessen muss man natürlich konstant debuggen.\n", "\n", "Wenn Ihnen auffällt, dass Sie viel Zeit mit Debugging verbringen, ist das oft ein Zeichen dafür, dass Sie zu viel Code schreiben, bevor Sie beginnen, diesen zu testen.\n", "Wenn Sie stattdessen kleinschrittiger arbeiten, ist es gut möglich, dass Sie insgesamt schneller vorankommen.\n", "\n", "\n", "\n", "Ein Beispiel ist **Linux**: ein Betriebssystem, welches auf Millionen von Rechnern verwendet wird und Millionen von Codezeilen enthält. Linux begann als ein einfaches Programm welches der finnische Student Linus Torvalds geschrieben hat, um den Intel 386 Prozessor besser zu verstehen. Laut Larry Greenfield war einer der ersten Versuche von Linus ein Programm, welches abwechselnd \"AAAA\" und \"BBBB\" ausgab. Daraus wurde später Linux. (*The Linux Users’ Guide Beta Version 1*). " ] }, { "cell_type": "markdown", "id": "d4e95e63", "metadata": { "id": "d4e95e63" }, "source": [ "## Glossar\n", "\n", "Legen wir uns eine Liste mit den wichtigsten Begriffen an, die wir im Kapitel 3 gelernt haben:\n", "\n", "- Funktion:\n", "- Funktionsaufruf:\n", "- Funktionsdefinition:\n", "- Kopf: Der Kopf einer Funktionsdefinition gibt den Namen der Funktion sowie alle geforderten Argumente in Klammern an und wird von einem Doppelpunkt abgeschlossen.\n", "- Rumpf:\n", "- Funktionsobjekt:\n", "- Parameter:\n", "- Schleife:\n", "- lokale Variable:\n", "- Stapel-Diagramm:\n", "- Block:\n", "- Traceback:\n", "\n", "Ergänzen Sie die Liste in eigenen Worten. Das ist eine gute Erinnerungs- und Übungsmöglichkeit.\n", "Sollten Sie einmal nicht weiterkommen, schauen Sie im Glossar von *Think Python* nach oder fragen Sie noch einmal nach." ] }, { "cell_type": "markdown", "id": "eca485f2", "metadata": { "id": "eca485f2" }, "source": [ "## Übung" ] }, { "cell_type": "code", "execution_count": null, "id": "3f77b428", "metadata": { "id": "3f77b428", "tags": [] }, "outputs": [], "source": [ "# Diese Zelle weist Jupyter ab, detallierte Debugging-Informationen auszugeben, wenn ein Laufzeitfehler\n", "# passiert. Lassen Sie sie daher laufen, bevor Sie beginnen an den Aufgaben zu arbeiten.\n", "\n", "%xmode Verbose" ] }, { "cell_type": "markdown", "id": "82951027", "metadata": { "id": "82951027" }, "source": [ "### Fragen Sie einen virtuellen Assistenten\n", "\n", "Die Anweisungen in einer Funktion oder einer `for`-Schleife sind üblicherweise vier Leerstellen eingerückt.\n", "Aber nicht alle in der Python-Community sind damit einverstanden.\n", "Wenn Sie neugierig über die Geschichte dieser großen Debatte sind, fragen Sie einen virtuellen Assistenten: \"erzähl mir etwas über Leerzeichen und Tabulatoren in Python\".\n", "\n", "Virtuelle Assistenten sind relativ gut darin, kleine Funktionen zu schreiben.\n", "\n", "1. Bitten Sie Ihren liebsten VA \"Schreib eine Funktion namens repeat, die eine Zeichenkette und eine ganze Zahl aufnimmt und die Zeichenkette eine bestimmte Anzahl von Malen ausgibt.\"\n", "\n", "2. Wenn das Ergebnis eine `for`-Schleife verwendet könnten Sie fragen: \"Kannst du das Gleiche ohne eine for-Schleife machen?\"\n", "\n", "3. Wählen Sie irgendeine der anderen Funktionen in diesem Kapitel und fragen Sie einen VA, diese zu schreiben. Die Herausforderung hierbei ist es, die Funktion genau genug zu beschreiben, um das gewünschte Ergebnis zu erhalten. Verwenden Sie das Vokabular, das Sie bislang in diesem Buch gelernt haben.\n", "\n", "Virtuelle Assistenten sind auch recht gut darin, Funktionen zu debuggen.\n", "\n", "1. Fragen Sie einen VA, was an dieser Version von `print_twice` falsch ist.\n", "\n", " ```\n", " def print_twice(string):\n", " print(cat)\n", " print(cat)\n", " ```\n", " \n", "Wenn Sie bei einer der Aufgaben unten nicht weiterkommen, denken Sie darüber nach, einen VA um Hilfe zu fragen." ] }, { "cell_type": "markdown", "id": "75ae6e1d-b5a2-4eb0-9362-db66f4d09b55", "metadata": {}, "source": [ "### Aufgabe 1\n", "\n", "\n", "#### Aufgabe 1a\n", "Diese Aufgabe ist etwas schwieriger, daher haben Sie zwei Möglichkeiten:\n", "\n", "1. Lösen Sie die Aufgabe selbständig.\n", "2. Scrollen Sie etwas herunter und lösen Sie die Aufgabe mit etwas Hilfe (\"Lückentext\").\n", "\n", "Schreiben Sie eine Funktion `right_justify` die eine Zeichenkette als Parameter `s` erwartet und diese Zeichenkette rechtsbündig ausgibt. Konkret heißt das, dass so viele Leerzeichen vor der Zeichenkette ausgibt, dass der letzte Buchstabe der Zeichenkette in Spalte 70 angezeigt wird. Es kann sein, dass Spalte 70 in Ihrer Ausgabe nicht ganz rechtsbündig ist. Das ist aber nicht schlimm, 70 kann trotzdem verwendet werden. Das folgende Beispiel zeigt einen \"rechtsbündigen\" Aufruf.\n", "\n", "Beispiel: wenn wir die fertige Funktion mit dem Wert `monty` aufrufen, soll Folgendes passieren:\n", "```python\n", "right_justify('monty')\n", " monty\n", "```\n", "\n", "Zunächst ein allgemeiner Hinweis, der Elemente von Python aufzeigt, die für die Lösung des Problems wichtig sind: Nutzen Sie die Zeichenkettenverknüpfung und -wiederholung. Python bietet uns auch eine Funktion an, mit der wir die Länge einer Zeichenkette ermitteln können. Diese Funktion heißt `len`. Sie erwartet als Argument eine Zeichenkette und gibt ihre Länge zurück. Der Rückgabewert von `len('monty')` ist also `5`.\n", "\n", "\n", "Jetzt ein paar Tipps, wie Sie das Problem angehen könnten. Lesen Sie nicht alles auf einmal durch, sondern nur jeweils einen Tipp und versuchen Sie dann erstmal wieder, das Problem zu zweit zu lösen. Wenn Sie mit diesen Tipps nicht weiterkommen, finden Sie ein wenig weiter unten einen Lückentext, den Sie vervollständigen sollen. Die beste Übung für Sie ist es, diese Aufgabe mit so wenigen Hinweisen wie möglich zu lösen.\n", "\n", "<details>\n", " <summary type=\"button\" class=\"btn btn-primary\">1. Hinweis</summary>\n", " <div class=\"alert alert-info\" role=\"alert\">\n", " \n", "Schreiben Sie zuerst den Funktionskopf. Welche Elemente benötigen Sie hier?\n", " \n", " </div> \n", "</details> \n", " \n", " \n", "<details>\n", " <summary type=\"button\" class=\"btn btn-primary\">2. Hinweis</summary>\n", " <div class=\"alert alert-info\" role=\"alert\">\n", "\n", "Lösen Sie die Aufgabe zunächst für die Zeichenkette 'monty' und verallgemeinern Sie diese Lösung anschließend.\n", "Wieviele Leerzeichen benötigen Sie, um 'monty' rechtsbündig auszugeben? (Falls Sie die Frage nicht beantworten können, lesen Sie sich die Aufgabe nochmal genau durch.)\n", " \n", " </div> \n", "</details> \n", " \n", "<details>\n", " <summary type=\"button\" class=\"btn btn-primary\">3. Hinweis</summary>\n", " <div class=\"alert alert-info\" role=\"alert\">\n", " \n", "Wie können Sie die Anzahl an benötigten Leerzeichen berechnen? Welche Werte und Funktionen benötigen Sie?\n", " \n", " </div> \n", "</details>\n", "\n", "<details>\n", " <summary type=\"button\" class=\"btn btn-primary\">4. Hinweis</summary>\n", " <div class=\"alert alert-info\" role=\"alert\">\n", " \n", "Geben Sie das Wort 'monty' rechtsbündig aus, verwenden Sie dabei nicht die konkret berechneten Zahlen, sondern die Berechnungen, die Sie durchgeführt haben. \n", " \n", " </div> \n", "</details>\n", "<details>\n", " <summary type=\"button\" class=\"btn btn-primary\">5. Hinweis</summary>\n", " <div class=\"alert alert-info\" role=\"alert\">\n", " \n", "Wenn Sie unsicher sind, wie Sie mehrere Elemente in einer 'print' Anweisung ausgeben können, schauen Sie sich noch einmal das 'print'- Tutorium aus dem 2. Notebook an. \n", " \n", " </div> \n", "</details>\n", "\n", "<details>\n", " <summary type=\"button\" class=\"btn btn-primary\">6. Hinweis</summary>\n", " <div class=\"alert alert-info\" role=\"alert\">\n", " \n", "Mit dem '*' Operator können Sie ein Leerzeichen beliebig oft ausgeben, nutzen Sie dies, um die korrekte Anzahl an Leerzeichen zu drucken, `' ' * 10` ergibt z.B. 10 Leerzeichen.\n", " \n", " </div> \n", "</details>\n", "<details>\n", " <summary type=\"button\" class=\"btn btn-primary\">7. Hinweis</summary>\n", " <div class=\"alert alert-info\" role=\"alert\">\n", " \n", "`' ' * 65 + 'monty'` wäre die Zeichenkette, die Sie für 'monty' bräuchten. Wie können Sie das allgemein formulieren, sodass es auch für den Parameter `s` (mit beliebiger Länge) funktioniert?\n", " \n", " </div> \n", "</details>\n", "<details>\n", " <summary type=\"button\" class=\"btn btn-primary\">8. Hinweis</summary>\n", " <div class=\"alert alert-info\" role=\"alert\">\n", " \n", "Ersetzen Sie 'monty' durch den Buchstaben s. (Dies müsste an 2 Stellen notwendig sein, einmal in der Berechnung der Leerzeichen und einmal in der print- Anweisung.)\n", " \n", " </div> \n", "</details>" ] }, { "cell_type": "code", "execution_count": null, "id": "7252df3b-ea8a-40e4-9c53-c4162e26fc46", "metadata": {}, "outputs": [], "source": [ "# Definieren Sie hier die Funktion right_justify:\n", "\n", "\n", "# (und testen Sie die Funktion zwischendurch immer wieder)" ] }, { "cell_type": "code", "execution_count": null, "id": "32fae5f4-c55d-4bd1-be9a-294df1e1929d", "metadata": {}, "outputs": [], "source": [ "# Wenn Sie fertig sind, rufen Sie die Funktion hier auf:\n", "right_justify(\"Monty Python's\")\n", "right_justify('The Ministry of Silly Walks')\n", "# (und testen Sie die Funktion zwischendurch immer wieder)" ] }, { "cell_type": "markdown", "id": "a856f395-143f-4561-af75-5761729e812d", "metadata": {}, "source": [ "Macht Ihre Funktion auch wirklich, was sie soll? Ein paar Ideen zum Testen:\n", "- Zählen Sie die Leerzeichen bis zum Wort. Das ist schwierig, da Leerzeichen schwer zu sehen sind. Zum Zählen können Sie das Leerzeichen durch ein anderes Zeichen — zum Beispiel einen Punkt — ersetzen.\n", "- Bei kurzen Wörtern sind es recht viele Punkte; als Abhilfe könnten Sie, statt in der 70. Spalte, alles rechtsbündig in der 10. Spalte ausgeben, also 70 Zeichen auf 10 reduzieren. Dann lässt es sich leichter zählen.\n", "\n", "Wenn Ihre Funktion so richtig funktioniert, sollte sie auch mit Leerzeichen statt Punkten und rechtsbündig in Spalte 70 statt Spalte 10 korrekt funktionieren. Das ist auch Debugging und Testen — es ist wichtig, um Fehler zu finden und hilft Ihnen, mit Ihrem Programm vertrauter zu werden." ] }, { "cell_type": "markdown", "id": "748d1fe1-59dd-4e32-ba6d-cbdebfa35118", "metadata": {}, "source": [ "Abschlussfrage: was passiert, wenn Sie die Funktion mit einer Zeichenkette aufrufen, die länger als 70 Zeichen ist? Probieren Sie es aus:" ] }, { "cell_type": "code", "execution_count": null, "id": "f14b346f-b53c-4962-b285-36f076003f12", "metadata": {}, "outputs": [], "source": [ "right_justify('Abschlussfrage: was passiert, wenn Sie die Funktion mit einer Zeichenkette aufrufen, die länger als 70 Zeichen ist?')" ] }, { "cell_type": "markdown", "id": "fa13eb91-8ee9-4f3a-884b-bb9ca1e769ca", "metadata": {}, "source": [ "##### Lückentext\n", "\n", "\n", "\n", "\n", "Bitte hier nur weiterlesen, wenn Sie weitere Hilfe benötigen. Versuchen Sie es aber vorher unbedingt erst einmal zu zweit. Es lohnt sich die Lösung selber herauszufinden; so lernen Sie am meisten dazu. \n", "\n", ". \n", "\n", ". \n", "\n", ". \n", "\n", "So sollte der Kopf Ihrer Funktion ausehen (alle notwendigen Informationen dazu stehen im ersten Teil des ersten Satzes der Aufgabe):\n", "\n", "```python\n", "def right_justify(s):\n", "```\n", "\n", ".\n", "\n", ". \n", "\n", ". \n", "\n", "Daraus resultiert dann dieser Lückentext:" ] }, { "cell_type": "code", "execution_count": null, "id": "4f74d380-b3fe-4a77-85bd-fca6ddf8f3d8", "metadata": {}, "outputs": [], "source": [ "def right_justify(s):\n", " # Anzahl Leerzeichen berechnen\n", " anzahl_leerzeichen = \n", " # Zeichenkette erzeugen\n", " ergebnis =\n", " # und ausgeben\n", " print()" ] }, { "cell_type": "markdown", "id": "8064d9e0-cd23-45f2-90ce-f0b91ce73dc7", "metadata": {}, "source": [ "\n", "([Spoiler Alert](https://xkcd.com/109/), Randall Munroe)\n", "\n", ".\n", "\n", ".\n", "\n", ".\n", "\n", "Wenn Sie Ihren Lösungsweg mit einer möglichen Lösung vergleichen wollen können Sie diese hier finden. Allerdings gibt es häufig mehr als einen Lösungsweg, wenn Ihre Lösung anders aussieht ist dies nicht zwingend falsch. Wenn Sie sich unsicher sind, fragen Sie bitte nach:" ] }, { "cell_type": "code", "execution_count": null, "id": "c2eebdef-7d7e-44e0-9771-481e56178245", "metadata": {}, "outputs": [], "source": [ "def right_justify(s):\n", " # Anzahl Leerzeichen berechnen\n", " anzahl_leerzeichen = 70 - len(s)\n", " # Zeichenkette erzeugen\n", " ergebnis = \" \" * anzahl_leerzeichen + s\n", " # und ausgeben\n", " print(ergebnis)" ] }, { "cell_type": "markdown", "id": "2c3ad3f0-5aae-4739-8f57-96863b1716b5", "metadata": {}, "source": [ "#### Aufgabe 1b\n", "Testen Sie folgendermaßen, ob Sie verstanden haben, wie die Funktion `right_justify` funktioniert: Schreiben Sie eine Funktion `center`, die eine als Argument übergebene Zeichenkette zentriert ausgibt (bei einer angenommen maximalen Spaltenbreite von 70, wie gerade eben):" ] }, { "cell_type": "markdown", "id": "64743221-703c-42d7-9f1e-b706e1cd8160", "metadata": {}, "source": [ "<details>\n", " <summary type=\"button\" class=\"btn btn-info\">Hinweis</summary>\n", " <div class=\"alert alert-info\" role=\"alert\">\n", " \n", "Mit dem Operator `//` können wir zwei ganze Zahlen so teilen, dass eine ganze Zahl (*integer*) herauskommt. Während `7 / 2` die Gleitkommazahl `3.5` ergibt, erhalten wir bei `7 // 2` die ganze Zahl `3` (es wird stets abgerundet).\n", " \n", " </div> \n", "</details> \n" ] }, { "cell_type": "markdown", "id": "01cb2d38-641b-4b23-938f-ba3a2b2333fa", "metadata": {}, "source": [ "Wie auch schon in der vorherigen Aufgabe finden Sie anschließend Hinweise, die einzelne Schritte der Lösung erklären und aufzeigen, versuchen Sie zunächst die Aufgabe ohne Hilfe zu lösen und schauen Sie die Hinweise wenn nötig Schritt für Schritt an." ] }, { "cell_type": "markdown", "id": "8b65a4f3-0b6a-4fe9-9097-4873883ddc83", "metadata": {}, "source": [ "<details>\n", " <summary type=\"button\" class=\"btn btn-primary\">1. Hinweis</summary>\n", " <div class=\"alert alert-info\" role=\"alert\">\n", " \n", "Diese Aufgabe ist eine Abwandlung der Aufgabe 1a, überlegen Sie an welcher Stelle die Aufgaben sich unterscheiden.\n", " \n", " \n", " </div> \n", "</details>\n", "\n", "<details>\n", " <summary type=\"button\" class=\"btn btn-primary\">2. Hinweis</summary>\n", " <div class=\"alert alert-info\" role=\"alert\">\n", " \n", "Benötigen Sie insgesamt die selbe Anzahl an Leerzeichen um die Zeichenkette zu zentrieren? Wie viele davon müssen vor der Zeichenkette stehen? Wie können Sie diese Anzahl berechnen?\n", " \n", " </div> \n", "</details> \n" ] }, { "cell_type": "code", "execution_count": null, "id": "9cf841d5-3c92-45fe-aee5-fa06292ee213", "metadata": {}, "outputs": [], "source": [ "# Schreiben Sie hier Ihre Funktion" ] }, { "cell_type": "markdown", "id": "d16fb48f-6316-447a-8311-bb436a01d39a", "metadata": {}, "source": [ "_Hinweis: Mit dem Operator `//` können wir zwei ganze Zahlen so teilen, dass eine ganze Zahl (*integer*) herauskommt. Während `7 / 2` die Gleitkommazahl `3.5` ergibt, erhalten wir bei `7 // 2` die ganze Zahl `3` (es wird stets abgerundet)._\n", "\n", "Ein Aufruf der Funktion mit den folgenden Argumenten:" ] }, { "cell_type": "code", "execution_count": null, "id": "15e1a5ae-193d-4f94-b5b6-29375fe3b135", "metadata": {}, "outputs": [], "source": [ "center('Diese Wörter')\n", "center('stehen')\n", "center('in')\n", "center('der Mitte')" ] }, { "cell_type": "markdown", "id": "ebf88c60-8f61-41b6-83fc-6f914889d977", "metadata": {}, "source": [ "sollte folgendes Ergebnis liefern:\n", "\n", "```\n", " Diese Wörter\n", " stehen\n", " in\n", " der Mitte\n", "```" ] }, { "cell_type": "markdown", "id": "34cabbc3-5753-40e9-891d-f1c81e505f33", "metadata": {}, "source": [ "\n", "([Spoiler Alert](https://xkcd.com/109/), Randall Munroe)\n", "\n", "\n", "Wenn Sie Ihren Lösungsweg mit einer möglichen Lösung vergleichen wollen, können Sie diese hier finden. Allerdings gibt es häufig mehr als einen Lösungsweg; wenn Ihre Lösung anders aussieht, ist dies nicht zwingend falsch. Wenn Sie sich unsicher sind, fragen Sie bitte nach:" ] }, { "cell_type": "code", "execution_count": null, "id": "32a5dac6-9d38-4ec8-b2b4-4f3c49486048", "metadata": {}, "outputs": [], "source": [ "def center(s):\n", " space=(70 - len(s)) // 2\n", " print(' ' * space + s)\n", " \n", "center (\"Das ist der Test\")" ] }, { "cell_type": "markdown", "id": "749e292f-a23d-489d-9839-bea85e0746df", "metadata": {}, "source": [ "### Aufgabe 2\n", "\n", "Ein Funktionsobjekt ist ein Wert, den wir einer Variablen zuweisen oder auch einer Funktion als Argument übergeben können. Zum Beispiel ist `do_twice` eine Funktion, die ein Funktionsobjekt als Argument erwartet und die Funktion dann zweimal aufruft:" ] }, { "cell_type": "code", "execution_count": null, "id": "5b9d1f50-7147-402f-9e01-40854ff2bc4a", "metadata": {}, "outputs": [], "source": [ "def do_twice(f):\n", " f()\n", " f()" ] }, { "cell_type": "markdown", "id": "f252465a-ed15-4c29-9a90-58bde470183a", "metadata": {}, "source": [ "Im Folgenden ein Beispiel, in dem die Funktion `do_twice` genutzt wird, um die Funktion `print_spam` zweimal aufzurufen:\n", "\n", "```python\n", "def print_spam():\n", " print('spam')\n", "\n", "do_twice(print_spam)\n", "```" ] }, { "cell_type": "markdown", "id": "c5364f2e-0c8d-416e-9c74-fd43ccd485be", "metadata": {}, "source": [ "<details>\n", " <summary type=\"button\" class=\"btn btn-info\">Hinweis</summary>\n", " <div class=\"alert alert-info\" role=\"alert\">\n", " \n", "Mit der Tastenkombination `SHIFT STRG -` können wir einen Block teilen - also `SHIFT` und `STRG` gleichzeitig gedrückt halten und dann die Minustaste drücken. So können Sie Ihren Code direkt hinter jeder der folgenden Teilaufgaben einfügen.\n", "\n", " </div> \n", "</details> " ] }, { "cell_type": "markdown", "id": "08343240-ef2d-461c-a6e3-b07d83201ea8", "metadata": {}, "source": [ "1. Geben Sie dieses Beispiel in einen Code-Block ein und testen Sie es.\n", "2. Ändern Sie `do_twice`, so dass \n", " - es zwei Argumente erwartet: ein Funktionsobjekt und einen Wert und\n", " - die übergebene Funktion zweimal aufruft und ihr den Wert als Argument übergibt.\n", " \n", "<details>\n", " <summary type=\"button\" class=\"btn btn-primary\">1. Hinweis</summary>\n", " <div class=\"alert alert-info\" role=\"alert\">\n", " \n", "Wenn die Funktion Argumente erwartet, müssen diese in den Funktionskopf eingefügt werden.\n", " </div> \n", "</details>\n", "\n", "<details>\n", " <summary type=\"button\" class=\"btn btn-primary\">2. Hinweis</summary>\n", " <div class=\"alert alert-info\" role=\"alert\">\n", " \n", "Die Argumente sollten in der Funktionsdefinition Platzhalter und daher Variablen sein.\n", "\n", " </div> \n", "</details>\n", "\n", "<details>\n", " <summary type=\"button\" class=\"btn btn-primary\">3. Hinweis</summary>\n", " <div class=\"alert alert-info\" role=\"alert\">\n", "\n", "Verwenden Sie dieselben Platzhaltervariablen im Rumpf der Funktion, um der Funktion zu sagen, was sie mit den Argumenten tun soll.\n", "\n", " </div> \n", "</details>\n", "\n", "<details>\n", " <summary type=\"button\" class=\"btn btn-primary\">4. Hinweis</summary>\n", " <div class=\"alert alert-info\" role=\"alert\">\n", "\n", "Haben Sie den Platzhalter für das 'Wertargument' an den Platzhalter der Funktion übergeben?\n", "\n", " </div> \n", "</details>\n", "\n", "\n", "3. Rufen Sie mit der geänderten Funktion `do_twice` die Funktion `print_twice` (die wir weiter vorne definiert hatten) auf und übergeben Sie ein Wort Ihrer Wahl als Argument.\n", "4. Definieren Sie eine Funktion `do_four`, die ein Funktionsobjekt und einen Wert erwartet und die übergebene Funktion viermal aufruft und ihr dabei den Wert als Parameter übergibt. Die Funktion `do_four` sollte dabei aus nur zwei Zeilen im Rumpf bestehen, nicht aus vier!\n", "\n", "<details>\n", " <summary type=\"button\" class=\"btn btn-primary\">1. Hinweis</summary>\n", " <div class=\"alert alert-info\" role=\"alert\">\n", " \n", "Schreiben Sie den Funktionsaufruf für `do-twice` und übergeben Sie die korrekte Anzahl an Argumenten.\n", " </div> \n", "</details>\n", "\n", "<details>\n", " <summary type=\"button\" class=\"btn btn-primary\">2. Hinweis</summary>\n", " <div class=\"alert alert-info\" role=\"alert\">\n", " \n", "Überlegen Sie wo genau Sie das Wort übergeben müssen, damit `print_twice` es innerhalb der Funktion `do-twice` verwenden kann.\n", " \n", " \n", " </div> \n", "</details>\n", "<details>\n", " <summary type=\"button\" class=\"btn btn-primary\">3. Hinweis</summary>\n", " <div class=\"alert alert-info\" role=\"alert\">\n", " \n", "Schreiben Sie `print_twice` und den Wert mit Kommas getrennt hintereinander in den Funktionsaufruf.\n", " </div> \n", "</details>\n", "\n", "\n", "4. Definieren Sie eine Funktion `do_four`, die ein Funktionsobjekt und einen Wert erwartet und die übergebene Funktion viermal aufruft und ihr dabei den Wert als Parameter übergibt. Die Funktion `do_four` sollte dabei aus nur zwei Zeilen im Rumpf bestehen, nicht aus vier! Rufen Sie die Funktion auf und testen Sie diese!\n", "\n", "<details>\n", " <summary type=\"button\" class=\"btn btn-primary\">1. Hinweis</summary>\n", " <div class=\"alert alert-info\" role=\"alert\">\n", " \n", "Wir überschreiben hier keine der bereits definierten Funktionen und können alle Funktionen, die wir schon verwendet haben, wiederverwenden.\n", " \n", " </div> \n", "</details>\n", "<details>\n", " <summary type=\"button\" class=\"btn btn-primary\">2. Hinweis</summary>\n", " <div class=\"alert alert-info\" role=\"alert\">\n", " \n", "Was passiert wenn Sie `do_twice` zweimal hintereinander ausführen? \n", " </div> \n", "</details>\n", "\n", "<details>\n", " <summary type=\"button\" class=\"btn btn-primary\">3. Hinweis</summary>\n", " <div class=\"alert alert-info\" role=\"alert\">\n", " \n", "Wie müssen Sie die Werte übergeben, sodass diese an der korrekten Stelle verwendet werden können? \n", " \n", " </div> \n", "</details>\n", "\n", "<details>\n", " <summary type=\"button\" class=\"btn btn-primary\">4. Hinweis</summary>\n", " <div class=\"alert alert-info\" role=\"alert\">\n", " \n", "`do_four` muss die Werte lediglich direkt an `do_twice` weiterreichen, `do_twice` sollte bereits korrekt mit den Werten umgehen, wenn Sie die Aufgabe schrittweise gelöst haben.\n", " \n", " </div> \n", "</details>" ] }, { "cell_type": "markdown", "id": "20a6727b-ffe9-43da-84bb-301c3c9c57cb", "metadata": {}, "source": [ "Hier finden Sie die Lösung. Versuchen Sie es erst einmal zu zweit und gehen Sie Schritt für Schritt vor!\n", "\n", "\n", "([Spoiler Alert](https://xkcd.com/109/), Randall Munroe)" ] }, { "cell_type": "code", "execution_count": null, "id": "7b7704b0-cfca-44a0-8db3-b10110b2cf51", "metadata": {}, "outputs": [], "source": [ "def print_spam():\n", " print('spam')\n", "\n", "do_twice(print_spam)\n" ] }, { "cell_type": "code", "execution_count": null, "id": "04d4264a-c464-4781-aa18-7225b9788eb5", "metadata": {}, "outputs": [], "source": [ "def do_twice(f,wert):\n", " f(wert)\n", " f(wert)" ] }, { "cell_type": "code", "execution_count": null, "id": "fd5f5312-e4b6-4b7d-91ca-f6ad1959eea9", "metadata": {}, "outputs": [], "source": [ "def print_twice(wert):\n", " print(wert)\n", " print(wert) " ] }, { "cell_type": "code", "execution_count": null, "id": "5a60cf32-79ef-4b9e-8000-cc48aff223e8", "metadata": {}, "outputs": [], "source": [ "do_twice(print_twice,\"test\")" ] }, { "cell_type": "code", "execution_count": null, "id": "b6fa0519-0b61-4989-83c6-2d428b82198c", "metadata": {}, "outputs": [], "source": [ "def do_four(f,wert):\n", " do_twice(f,wert)\n", " do_twice(f,wert)" ] }, { "cell_type": "markdown", "id": "bd7be38b-d8bd-46ba-803b-a304198728ac", "metadata": {}, "source": [ "### Aufgabe 3\n", "\n", "<details>\n", " <summary type=\"button\" class=\"btn btn-info\">Hinweis</summary>\n", " <div class=\"alert alert-info\" role=\"alert\">\n", " \n", "Nutzen Sie nur Anweisungen und Funktionen, die wir bereits kennengelernt haben.\n", " \n", " </div> \n", "</details>\n", "\n", "\n", "#### Aufgabe 3.1\n", "\n", "Schreiben Sie eine Funktion, die das folgende Gitter \"zeichnet\":\n", "\n", "```\n", "+ - - - - + - - - - +\n", "| | |\n", "| | |\n", "| | |\n", "| | |\n", "+ - - - - + - - - - +\n", "| | |\n", "| | |\n", "| | |\n", "| | |\n", "+ - - - - + - - - - +\n", "```\n", "<details>\n", " <summary type=\"button\" class=\"btn btn-info\">Hinweis</summary>\n", " <div class=\"alert alert-info\" role=\"alert\">\n", " \n", "Zeichenketten lassen sich durch den Plus-Operator `+` verbinden.\n", "Außerdem nimmt die `print()`-Funktion auch mehrere Argumente entgegen. Diese müssen durch Kommata getrennt werden:\n", "`print(\"+\", Variable, \"-\")`.\n", " \n", " </div> \n", "</details>\n", "\n", "\n", "\n", "------------------------\n", "\n", "\n", "Wenn Sie nicht wissen, wie Sie an die Aufgabe herangehen können, können Sie die folgenden Hinweise nutzen um den Anfang zu finden. Schauen Sie sich dabei immer so wenige Hinweise an, wie möglich.\n", "\n", "\n", "<details>\n", " <summary type=\"button\" class=\"btn btn-primary\">1. Hinweis</summary>\n", " <div class=\"alert alert-info\" role=\"alert\">\n", " \n", "Schreiben Sie die verschiedenen Zeilentypen auf, die Sie für das Gitter brauchen.\n", " \n", " </div> \n", "</details>\n", "\n", "\n", "<details>\n", " <summary type=\"button\" class=\"btn btn-primary\">2. Hinweis</summary>\n", " <div class=\"alert alert-info\" role=\"alert\">\n", " \n", "Überlegen Sie sich, in welchem Muster die verschiedenen Zeilentypen angeordnet sind. \n", " </div> \n", "</details>\n", "\n", "<details>\n", " <summary type=\"button\" class=\"btn btn-primary\">3. Hinweis</summary>\n", " <div class=\"alert alert-info\" role=\"alert\">\n", " \n", "Überlegen Sie sich, in welchem Muster die verschiedenen Zeilentypen angeordnet sind. Haben Sie bereits eine Funktion definiert, mit der Sie dieselbe Zeile mehrfach hintereinander ausgeben lassen können? \n", " </div> \n", "</details>\n", "\n", "<details>\n", " <summary type=\"button\" class=\"btn btn-primary\">4. Hinweis</summary>\n", " <div class=\"alert alert-info\" role=\"alert\">\n", " \n", " Wenn Sie eine Funktion definieren, die genau eine der Seiten (`| | |`) ausgibt, können Sie `do_four()` verwenden, um diese viermal hintereinander ausgeben zu lassen. Sie können sich dann noch überlegen, ob Sie die Querbalken manuell in der `grid()` Funktion aufschreiben oder ob Sie diese auch in eine Funktion packen und dann an der entsprechenden Stelle diese Funktion aufrufen. \n", " </div> \n", "</details>\n", "\n", "\n", "Beachten Sie bitte, dass es fast immer mehr als einen Weg gibt, die Aufgaben zu lösen. Solange die Ausgabe stimmt, haben Sie wahrscheinlich alles richtig gemacht.\n", "\n" ] }, { "cell_type": "code", "execution_count": null, "id": "77b04b4b-f712-4acc-8c8d-c697400c00a7", "metadata": {}, "outputs": [], "source": [] }, { "cell_type": "markdown", "id": "ffee3269-964f-48f2-81ad-045127d40eba", "metadata": {}, "source": [ "#### Aufgabe 3.2 \n", "\n", "Schreiben Sie eine ähnliche Funktion, die ein Gitter mit je 4 Reihen und Zeilen zeichnet.\n", "\n", "Bevor Sie anfangen Ihre eigene Lösung zu entwickeln, schauen Sie sich den folgenden Code an und versuchen Sie zu verstehen, was er macht. Führen Sie ihn aus und beschreiben Sie, warum er die Aufgabe nicht löst." ] }, { "cell_type": "code", "execution_count": null, "id": "3a3fb4a3-0e6d-41dc-8255-f9f0878948f6", "metadata": {}, "outputs": [], "source": [ "def do_two(f):\n", " f()\n", " f()\n", " \n", "def setup():\n", " print(\"+ - + - +\")\n", " print(\"| | |\")\n", "\n", "def minigrid():\n", " do_two(setup)\n", " print(\"+ - + - +\")\n", " \n", "minigrid()\n" ] }, { "cell_type": "markdown", "id": "22653d1d-1963-455f-bfd7-df5f36d263ec", "metadata": {}, "source": [ "<details>\n", " <summary type=\"button\" class=\"btn btn-success\">Lösung</summary>\n", " <div class=\"alert alert-success\" role=\"alert\">\n", " \n", "Hier wird ein Gitter gezeichnet, welches auch aus 4 Kästchen besteht, die aber viel kleiner sind als die Kästchen in Aufgabenteil 1. In dieser Aufgabe wird nicht verlangt, dass die Kästchengröße verändert wird. Es sollen stattdessen insgesamt 16 Kästchen gezeichnet werden, das Muster soll sich also vergrößern, nicht verkleinern. \n", " </div> \n", "</details>\n", "\n", "\n", "Wenn Sie verstanden haben, warum die obige Lösung nicht korrekt ist, können Sie versuchen, eine korrekte Lösung zu entwickeln.\n", "\n", "\n", "<details>\n", " <summary type=\"button\" class=\"btn btn-primary\">1. Hinweis</summary>\n", " <div class=\"alert alert-info\" role=\"alert\">\n", " \n", "Überlegen Sie zunächst, wie das gewünschte Gitter aussehen soll. Wie unterscheidet es sich von dem ersten Gitter? Erstellen Sie eventuell per Hand eine grobe Skizze, damit Sie sich anschauen können, wo sich das Muster wiederholt. \n", " \n", " </div> \n", "</details>\n", "\n", "\n", "<details>\n", " <summary type=\"button\" class=\"btn btn-primary\">2. Hinweis</summary>\n", " <div class=\"alert alert-info\" role=\"alert\">\n", " \n", "Zuerst müssen wir die beiden Zeilentypen korrekt erstellen. Dafür könnten wir schlicht und ergreifend jeweils die Zeile einmal aufschreiben. Dieser Ansatz ist aber sehr fehleranfällig, stattdessen ist es besser, wenn wir mit der Verkettung durch `*` und `+` arbeiten.\n", " </div> \n", "</details>\n", "\n", "\n", "<details>\n", " <summary type=\"button\" class=\"btn btn-primary\">3. Hinweis</summary>\n", " <div class=\"alert alert-info\" role=\"alert\">\n", " \n", "Um die Zeile optimiert aufzuschreiben, können Sie eine Variable erstellen und ihr das erste Viertel der Zeile zuweisen. Dann können Sie mit Hilfe von Verkettung dieses Viertel vier mal ausgeben und mit dem Schlusszeichen abschließen.\n", " \n", " </div> \n", "</details>\n", "\n", "<details>\n", " <summary type=\"button\" class=\"btn btn-primary\">4. Hinweis</summary>\n", " <div class=\"alert alert-info\" role=\"alert\">\n", " \n", "Überlegen Sie sich, was Sie aus der vorherigen Lösung übernehmen können, um das Gitter darzustellen. Wie müssen Sie die Zeilenwiederholung anpassen, damit das korrekte Ergebnis angezeigt wird?\n", " \n", " </div> \n", "</details>\n", "\n", "<details>\n", " <summary type=\"button\" class=\"btn btn-primary\">5. Hinweis</summary>\n", " <div class=\"alert alert-info\" role=\"alert\">\n", " \n", "Sie müssen je 4 mal *den horizontal Balken gefolgt von 4 Seitenwänden* ausgeben lassen und zum Schluss noch einen horizontalen Balken zeichnen.\n", " \n", " </div> \n", "</details>\n", "\n", "<details>\n", " <summary type=\"button\" class=\"btn btn-primary\">6. Hinweis</summary>\n", " <div class=\"alert alert-info\" role=\"alert\">\n", " \n", "Auch wenn diese Version funktioniert, ist sie doch nicht sehr effektiv und wir können Sie verbessern. Eine Folge von Anweisungen wird 4 mal ausgeführt. Wenn wir diese Folge in eine eigene Funktion packen, können wir `do_four()` verwenden, um diese Folge viermal auszuführen und unseren Code somit beträchtlich kürzen.\n", " \n", " </div> \n", "</details>\n", "\n", "\n" ] }, { "cell_type": "markdown", "id": "789214cc-97fa-41e5-ba48-09d0d4ee86ff", "metadata": {}, "source": [ "Wie gehabt, folgt jetzt die Lösung. Lesen Sie erst weiter, wenn Sie die Aufgabe gelöst haben und sich eine Musterlösung anschauen wollen. Nutzen Sie zunächst die Hinweise, die Sie weiter oben im Text finden, um Ihre eigene Lösung zu entwickeln.\n", "\n", "\n", "([Spoiler Alert](https://xkcd.com/109/), Randall Munroe)\n", "\n", "Zuerst ein paar Tipps, um die Aufgabe selber zu lösen. Lesen Sie nicht alle Tipps auf einmal durch:\n", "\n", "1. Welche Zeilen müssen wir überhaupt ausgeben? Schreiben Sie die Zeilen einzeln auf.\n", "2. Wie sieht das Gitter aus? Können Sie es mit einer Reihe von `print`-Statements darstellen?\n", "3. Welche Zeilen wiederholen sich? Können Sie diese mit Hilfe von Funktionen zusammenfassen?\n", "\n", "\n", "Dies ist nur eine mögliche Lösung, weitere Lösungsmöglichkeiten finden sie [hier](http://greenteapress.com/thinkpython2/code/grid.py)" ] }, { "cell_type": "code", "execution_count": null, "id": "34abd48a-33b9-4bd8-ba27-096deb935019", "metadata": {}, "outputs": [], "source": [ "#Für Aufgabe 3.1 ein 2*2 Grid:\n", "\n", "# Wir verwenden vorher definierte Funktionen, um uns Aufwand zu sparen.\n", "def do_twice(f):\n", " f()\n", " f()\n", "\n", "def do_four(f):\n", " do_twice(f)\n", " do_twice(f)\n", "\n", "def print_innerSide(): \n", " '''Die Funktion definiert eine der mittleren Seiten (| | |) und gibt diese aus.'''\n", " print(\"|\" + \" \" * 9 + \"|\" + \" \" * 9 + \"|\")\n", " \n", "def print_edge(): \n", " '''Die Funktion definiert die Kanten (+ - - - - + - - - - +) und gibt diese aus.''' \n", " print (\"+ \" + \"- \" * 4 + \"+ \" + \"- \" * 4 + \"+\") \n", " \n", " \n", "def print_grid():\n", " '''Wir nutzen die vorher definierten Funktionen, um das Gitter zu definieren und auszugeben.''' \n", " print_edge()\n", " do_four(print_innerSide)\n", " print_edge()\n", " do_four(print_innerSide)\n", " print_edge()\n", " \n", "print_grid()" ] }, { "cell_type": "markdown", "id": "b47467fa", "metadata": { "id": "b47467fa" }, "source": [ "### Aufgabe 4\n", "\n", "Schreiben Sie eine Funktion namens `triangle`, die eine _Zeichenkette_ und eine _ganze Zahl_ aufnimmt und mit der gegebenen _Höhe_ eine Pyramide zeichnet, die aus _Kopien der Zeichenkette_ aufgebaut ist. Hier ist ein Beispiel für eine Pyramide mit `5` Stufen, die die Zeichenkette `'L'` verwendet:\n", "```\n", " L\n", " LLL\n", " LLLLL\n", " LLLLLLL\n", " LLLLLLLLL\n", "```" ] }, { "cell_type": "code", "execution_count": null, "id": "a5a3a8bb-4584-47c3-9599-b00991d6acf8", "metadata": {}, "outputs": [], "source": [ "# Lösung kommt hierhin" ] }, { "cell_type": "code", "execution_count": null, "id": "b8146a0d", "metadata": { "id": "b8146a0d", "tags": [] }, "outputs": [], "source": [ "triangle('L', 5)" ] }, { "cell_type": "markdown", "id": "1a7dca79-e316-4329-bf91-5b65932f47af", "metadata": {}, "source": [ "\n", "([Spoiler Alert](https://xkcd.com/109/), Randall Munroe)" ] }, { "cell_type": "markdown", "id": "5ddcfa7c-638e-4e3a-8872-d4d848453dd7", "metadata": {}, "source": [ "In dieser Aufgabe müssen wir folgende Probleme lösen:\n", "1. Ermittlung der Anzahl der Zeichen je nach Stufe im Dreieck\n", "2. Ermittlung der jeweiligen Anzahl der Leerzeichen vor den Zeichen\n", "\n", "Das 1. Problem ist unabhängig von der Gesamthöhe des Dreiecks, sondern lediglich von der Stufe abhängig. Das 2. Problem hängt sowohl von der Stufe des Dreiecks, als auch von der Höhe bzw. Breite des Dreiecks ab, zumindest wenn wir immer ganz links mit der Ausgabe beginnen möchten. Zum Verständnis habe ich daher zunächst die Breite des Dreiecks an der unteren Kante berechnet, auch wenn diese Werte nicht zwingend benötigt werden. Unter der Annahme, dass die Zeichenlänge immer 1 ist (also bspw. `letter='L'` nicht `letter='LL'`) erhalten wir folgende Werte:\n", "| Höhe (H) | Breite (B) | B - H |\n", "|------|--------|--------|\n", "| 2 | 3 | 1\n", "| 3 | 5 | 2\n", "| 4 | 7 | 3\n", "| 5 | 9 | 4\n", "\n", "Wenn wir uns die Tabelle ansehen, stellen wir fest, dass die Breite immer eine Einheit weniger ist als die verdoppelte Höhe. Damit können wir festhalten: `b = h * 2 - 1`. Falls wir auch noch längere Zeichenketten berücksichtigen wollen, können wir die Länge der Eingabe-Zeichenkette `s` hiermit multiplizieren: `b = (h * 2 - 1) * len(s)`.\n", "\n", "Nun zurück zum Problem. Wir suchen die Anzahl der Zeichen `ns` (1. Problem) und die Anzahl der Leerzeichen `nl` (2. Problem) je nach *Iteration* in der for-Schleife bzw. der Höhe des Dreiecks. Zu beachten ist, dass wir im Dreieick von oben nach unten iterieren, d.h. `i=0` ist die höchste Stufe (oder Spitze) des Dreiecks usw.\n", "\n", "| Iteration (`i`) | `ns` | `nl` (`h=5`)| `nl` (`h=4`) | `nl` (`h=3`) | `nl` (`h=4`) |\n", "|------|--------|--------|--------|--------|--------|\n", "| 0 | 1 | 4 | 3 | 2 | 1 |\n", "| 1 | 3 | 3 | 2 | 1 | 0 |\n", "| 2 | 5 | 2 | 1 | 0 | - |\n", "| 3 | 7 | 1 | 0 | - | - |\n", "| 4 | 9 | 0 | - | - | - | \n", "\n", "Wenn wir in die jeweils erste Spalte schauen bei den Werten für `nl` können wir sehen, dass diese Werte immer ein Wert weniger sind als die jeweilige Höhe der Dreiecke. Zudem werden diese Werte dann immer um 1 niedriger, genau wie `i` um 1 erhöht wird. Wir können daher definieren: `nl = (h - 1 - i)` bzw. mit Rücksichtnahme der Länge von `s`: `nl = (h - 1 - i) * len(s)`. Nun müssen wir noch die Anzahl der Zeichen ermitteln, wie in Problem 1 definiert. Wir können in der Tabelle sehen, dass der Wert immer um zwei mehr wird, allerdings bei der ersten Iteration eine 1 ist. Als elegante Lösung können wir die Anzahl als Ausdruck `ns = i * 2 + 1`, denn bei der ersten *Iteration* wird lediglich die 1 auf die 0 addiert und danach kommt immer eine 1 auf die Verdopplung der *Iteration*. Nun müssen wir lediglich beide Werte Werte in der *for-Schleife* implementieren:\n" ] }, { "cell_type": "code", "execution_count": null, "id": "eb2164cc-acb4-46f6-8782-c047f4f80c57", "metadata": {}, "outputs": [], "source": [ "def triangle(s, h):\n", " # Breite berechnen\n", " for i in range(h):\n", " # berechne Anzahl der notwendigen Zeichen\n", " ns = i * 2 + 1\n", " # berechne Anzahl der notwendigen Leerzeichen davor\n", " nl = (h - 1 - i) * len(s) \n", " # Ausgabe\n", " print(nl * ' ' + ns * s)\n", " " ] }, { "cell_type": "markdown", "id": "6934360d-c52b-4447-9a2b-3a31e0d06bcc", "metadata": {}, "source": [ "Die angegebene Lösung funktioniert dann auch mit längeren Zeichenketten:" ] }, { "cell_type": "code", "execution_count": null, "id": "80fa34b8-7f45-40af-badf-1f758352e890", "metadata": {}, "outputs": [], "source": [ "triangle(\"HT\", 6)\n" ] }, { "cell_type": "markdown", "id": "4a28f635", "metadata": { "id": "4a28f635" }, "source": [ "### Aufgabe 5\n", "\n", "Schreiben Sie eine Funktion namens `rectangle`, die eine _Zeichenkette_ und _zwei ganze Zahlen_ aufnimmt und mit der gegebenen _Breite_ und _Höhe_ ein Rechteck zeichnet, das aus _Kopien der Zeichenkette_ aufgebaut ist. Hier ist ein Beispiel für eine Rechteck mit der Breite `5` und der Höhe `4`, das die Zeichenkette `'H'` verwendet:\n", "```\n", "HHHHH\n", "H H\n", "H H\n", "HHHHH\n", "```" ] }, { "cell_type": "code", "execution_count": null, "id": "bcedab79", "metadata": { "id": "bcedab79" }, "outputs": [], "source": [ "# Lösung kommt hierhin" ] }, { "cell_type": "code", "execution_count": null, "id": "73b0c0f6", "metadata": { "id": "73b0c0f6", "tags": [] }, "outputs": [], "source": [ "rectangle('H', 5, 4)" ] }, { "cell_type": "markdown", "id": "f52c107d-d7e3-407e-90f0-e72b5698866d", "metadata": {}, "source": [ "\n", "([Spoiler Alert](https://xkcd.com/109/), Randall Munroe)" ] }, { "cell_type": "code", "execution_count": null, "id": "75d47004-7044-49da-b071-1485cc6cc547", "metadata": {}, "outputs": [], "source": [ "def rectangle(letter, width, height):\n", " # Ober und Unterkante definieren\n", " upperlower = letter * width\n", "\n", " # Zeilen für die Seitenkanten\n", " nspaces = len(upperlower) - 2 * len(letter)\n", " inner = letter + nspaces * ' ' + letter\n", "\n", " # Rechteck ausgeben\n", " print(upperlower)\n", " for i in range(height - 2):\n", " print(inner)\n", " print(upperlower)\n", " " ] }, { "cell_type": "markdown", "id": "44a5de6f", "metadata": { "id": "44a5de6f" }, "source": [ "### Aufgabe 6\n", "\n", "Das Lied \"99 Bottles of Beer\" beginnt mit dieser Strophe:\n", "\n", "> 99 bottles of beer on the wall \n", "> 99 bottles of beer \n", "> Take one down, pass it around \n", "> 98 bottles of beer on the wall \n", "\n", "Die zweite Strophe ist gleich, außer dass sie mit \"98 bottles\" beginnt und mit 97 endet. Das Lied geht weiter -- für eine sehr lange Zeit -- bis nur noch \"0 bottles of beer\" übrig sind.\n", "\n", "Schreiben Sie eine Funktion namens `bottle_verse`, die eine Nummer als Parameter aufnimmt und die Strophe, die mit dieser Nummer beginnt ausgibt.\n", "\n", "Tipp: Denken Sie darüber nach, zuerst eine Funktion zu schreiben, die die erste, zweite oder letzte Zeile der Strophe ausgibt und verwenden Sie diese anschließend, um `bottle_verse`zu schreiben:" ] }, { "cell_type": "code", "execution_count": null, "id": "53424b43", "metadata": { "id": "53424b43" }, "outputs": [], "source": [ "# Lösung kommt hierhin" ] }, { "cell_type": "code", "execution_count": null, "id": "61010ffb", "metadata": { "id": "61010ffb" }, "outputs": [], "source": [ "# Lösung kommt hierhin" ] }, { "cell_type": "markdown", "id": "ee0076dd", "metadata": { "id": "ee0076dd", "tags": [] }, "source": [ "Verwenden Sie diesen Funktionsaufruf, um die erste Strophe darzustellen:" ] }, { "cell_type": "code", "execution_count": null, "id": "47a91c7d", "metadata": { "id": "47a91c7d", "tags": [] }, "outputs": [], "source": [ "bottle_verse(99)" ] }, { "cell_type": "markdown", "id": "42c237c6", "metadata": { "id": "42c237c6", "tags": [] }, "source": [ "Wenn Sie den vollständigen Song abbilden möchten, können Sie diese `for`-Schleife verwenden, die von `99` bis `1` herabzählt.\n", "Sie müssen dieses Beispiel noch nicht vollständig verstehen---wir werden später noch mehr über die `for`-Schleife und die `range`-Funktion lernen." ] }, { "cell_type": "code", "execution_count": null, "id": "336cdfa2", "metadata": { "id": "336cdfa2", "tags": [] }, "outputs": [], "source": [ "for n in range(99, 0, -1):\n", " bottle_verse(n)\n", " print()" ] }, { "cell_type": "markdown", "id": "f2b1ce35-e688-4437-bd47-e945adae7eb0", "metadata": {}, "source": [ "\n", "([Spoiler Alert](https://xkcd.com/109/), Randall Munroe)" ] }, { "cell_type": "code", "execution_count": null, "id": "62ed5896-d9a4-4e11-b03f-d9e7ff1b7b4c", "metadata": {}, "outputs": [], "source": [ "def bottle_verse(n):\n", " print(str(n) + \" bottles of beer on the wall\")\n", " print(str(n) + \" bottles of beer\")\n", " print(\"Take one down, pass it around\")\n", " print(str(n - 1) + \" bottles of beer on the wall\")\n", " " ] }, { "cell_type": "markdown", "id": "a51644e6-5eb5-4f29-8755-420764d92d9a", "metadata": {}, "source": [ "### Bonusaufgabe\n", "\n", " Finden Sie einen Algorithmus zur Lösung des folgenden Problems:\n", " *Gegeben sei eine positive ganze Zahl `n`. Finden Sie eine Liste\n", " von positiven ganzen Zahlen, so dass das Produkt der Zahlen am\n", " größten unter allen positiven ganzen Zahlen ist, deren Summe\n", " gleich `n` ist.*\n", "\n", " Zum Beispiel: \n", " - Für `n = 4` ist die gesuchte Liste `(2,2)`, denn `2 * 2` ist größer als `1 * 1 * 1 * 1`, `2 * 1 * 1` und `3 * 1`.\n", " - Für `n = 5` ist die gesuchte Liste `(2,3)`.\n", "\n", "Erklären Sie, wie Sie \"einen Fuß in die Tür bekommen\" haben.\n", "\n", "Wie lautet die Liste für `n = 2001`?\n", "\n", "\n", "\n", "Versuchen Sie es zunächst ohne Hilfe. Wie kann Ihnen Python dabei helfen? \n", "\n", ".\n", "\n", ".\n", "\n", ".\n", "\n", "\n", "([Spoiler Alert](https://xkcd.com/109/), Randall Munroe)\n", "\n", ".\n", "\n", ".\n", "\n", ".\n", "\n", "Mein \"Fuß in der Tür\" war, dass ich von Hand eine Tabelle mit den Ergebnissen für die ersten `n` Zahlen gebaut habe:\n", "\n", "| n | Liste | Produkt |\n", "|----|-----------|---------|\n", "| 2 | 1 1 | 1 | \n", "| 3 | 1 2 | 2 | \n", "| 4 | 2 2 | 4 |\n", "| 5 | 2 3 | 6 |" ] }, { "cell_type": "markdown", "id": "f19313e3-0193-42fd-bb11-c7b3868c0e19", "metadata": {}, "source": [ "Ergänzen Sie diese Tabelle von Hand. Dabei kann Ihnen eine Funktion helfen, die für eine gegebene Liste an Zahlen das Produkt und die Summe berechnet:" ] }, { "cell_type": "code", "execution_count": null, "id": "d6fe77fc-429f-4b47-be8b-7aac05b097dc", "metadata": {}, "outputs": [], "source": [ "def prodsum(zahlen):\n", " prod = 1\n", " summ = 0\n", " for zahl in zahlen:\n", " prod = prod * zahl\n", " summ = summ + zahl\n", " print(\"Produkt =\", prod, \"Summe =\", summ)" ] }, { "cell_type": "markdown", "id": "47da2cc7-8742-435c-beaa-326abba88e73", "metadata": {}, "source": [ "Eine Liste von Zahlen können wir erzeugen, indem wir die Zahlen durch Komma getrennt zwischen zwei Klammern schreiben:" ] }, { "cell_type": "code", "execution_count": null, "id": "5b31af5e-304b-4ad6-8353-b36986da63f6", "metadata": {}, "outputs": [], "source": [ "(2,3)" ] }, { "cell_type": "markdown", "id": "df15896b-ae7c-47b8-a3c9-cfaaf5677023", "metadata": {}, "source": [ "Wir können also `prodsum` so aufrufen:" ] }, { "cell_type": "code", "execution_count": null, "id": "96c3e0c9-49da-4f97-978f-2e9ae12e23c8", "metadata": {}, "outputs": [], "source": [ "prodsum((2,3))" ] }, { "cell_type": "markdown", "id": "e37ec127-5844-46ca-b78a-1b05a02b92ab", "metadata": {}, "source": [ "Testen Sie für jedes `n` mehrere Listen, bis Sie sich jeweils sicher sind, die mit dem größten Produkt gefunden zu haben.\n", "\n", ".\n", "\n", ".\n", "\n", ".\n", "\n", "Ich habe mit Hilfe der Funktion die Tabelle bis `n=15` ergänzt bis ich mir sicher war, dass ich das Prinzip verstanden hatte. \n", "\n", ".\n", "\n", ".\n", "\n", ".\n", "\n", ".\n", "\n", ".\n", "\n", ".\n", "\n", "Sehen Sie jetzt ein Muster in der Tabelle? Die Produkte bestehen nur aus 3en und ggf. noch 2en oder 4en. Genauer:\n", "\n", "Beobachtung: \n", "- Ein Produkt aus möglichst vielen 3en ergibt das beste Ergebnis.\n", "- Falls es nicht ganz aufgeht, mit 2 oder 4 auffüllen.\n", "\n", "Erklärung:\n", "- Ob wir eine 4 oder zwei 2en nehmen, ist egal, da `2+2 = 4 = 2*2`.\n", "- Da `2+3=5` aber `2*3=6`, lohnt es sich nicht, größere Zahlen zu nehmen\n", " (ebenso: `3+3=6` aber `3*3=9`) - das Produkt der kleinen Zahlen ist stets größer als ihre Summe\n", "\n", "Algorithmus:" ] }, { "cell_type": "code", "execution_count": null, "id": "bfcaf0de-9924-4f7d-903d-6c944b7c923a", "metadata": {}, "outputs": [], "source": [ "def produkt_summe(n):\n", " \"\"\"Berechnet für gegebenes n>2 das Produkt derjenigen Liste von\n", " Zahlen, deren Summe n ergibt und gleichzeitig die größte Liste mit\n", " dieser Eigenschaft ist.\n", " \n", " Vorgehen: wiederholt 3 von n abziehen, bis der Rest kleiner oder \n", " gleich 4 ist. (letzter Schritt klappt, weil 2+2=4=2*2)\n", "\n", " \"\"\"\n", " rest = n\n", " prod = 1\n", " zahlen = []\n", " while rest > 4:\n", " rest = rest - 3\n", " prod = prod * 3\n", " zahlen.append(3)\n", " prod = prod * rest\n", " zahlen.append(rest)\n", " \n", " print(\"*\".join([str(z) for z in zahlen]), \"=\", prod)\n", " print(\"+\".join([str(z) for z in zahlen]), \"=\", sum(zahlen))" ] }, { "cell_type": "markdown", "id": "00cccd12-9dfe-4157-8b93-78900c649339", "metadata": {}, "source": [ "Testen wir es einmal aus:" ] }, { "cell_type": "code", "execution_count": null, "id": "3f3cebbc-3145-46ef-922f-906aaf492b52", "metadata": {}, "outputs": [], "source": [ "produkt_summe(14)" ] }, { "cell_type": "markdown", "id": "6fbf3969-0ee5-4de7-9f30-3eec73f7095d", "metadata": {}, "source": [ "Das sollte auch die Zahl sein, die Sie für `n=14` oben in Ihrer Tabelle stehen haben. \n", "\n", "Die Funktion verwendet zwar ein paar neue Dinge, um eine schöne Ausgabe zu erzeugen, aber die wesentliche Funktionalität in der `while`-Schleife zur Berechnung des Produkts besteht nur aus Konstrukten, die wir schon kennengelernt haben. \n", "\n", "\n", "\n", "\n", "([Python](https://xkcd.com/353/), Randall Munroe)\n", "\n" ] }, { "cell_type": "markdown", "id": "b436a292-a82a-42f6-8215-73ccc22b568c", "metadata": { "id": "91e5a869" }, "source": [ " Speichern Sie dieses Notebook, so dass Ihre Änderungen nicht verlorengehen (nicht auf einem Pool-Rechner). Rufen Sie dazu im Menü *File* den Punkt *Download as* → *Notebook* auf und nutzen Sie beispielsweise einen USB-Stick, E-Mail, Google Drive, Dropbox oder Ihre [HU-Box](https://box.hu-berlin.de/). \n" ] }, { "cell_type": "markdown", "id": "b16effcd-b41f-4c2e-a789-c63e9840d1c4", "metadata": {}, "source": [ "\n", "\n", "Herzlichen Glückwunsch! Sie haben das 3. Kapitel geschafft. " ] }, { "cell_type": "markdown", "id": "02d2ccdb-c3fa-4db0-b967-602f20b6f080", "metadata": {}, "source": [ "<img src=\"https://scm.cms.hu-berlin.de/ibi/python/-/raw/master/img/by-nc-sa.png\" alt=\"CC BY-NC-SA\" style=\"width: 150px\"/>\n", "\n", "Der Text dieses Notebooks ist als freies Werk unter der Lizenz [CC BY-NC-SA 4.0 ](https://creativecommons.org/licenses/by-nc-sa/4.0/) verfügbar.\n", "Der Code dieses Notebooks ist als freies Werk unter der Lizenz [MIT License](https://mit-license.org/) verfügbar.\n", "\n", "Es handelt sich um übersetzte und leicht veränderte Notebooks von [Allen B. Downey](https://allendowney.com) aus [Think Python: 3rd Edition](https://allendowney.github.io/ThinkPython/index.html).\n" ] } ], "metadata": { "celltoolbar": "Tags", "colab": { "provenance": [], "toc_visible": true }, "kernelspec": { "display_name": "Python 3 (ipykernel)", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.12.2" } }, "nbformat": 4, "nbformat_minor": 5 }