Newer
Older
{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"[Chapter 6: Fruitful function](http://greenteapress.com/thinkpython2/html/thinkpython2007.html)\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). [Erklärung der Comics](https://www.explainxkcd.com/wiki/index.php/221:_Random_Number) falls Sie mehr erfahren wollen.\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"<h1>Inhaltsverzeichnis<span class=\"tocSkip\"></span></h1>\n",
"<div class=\"toc\"><ul class=\"toc-item\"><li><span><a href=\"#Ihre-Lernziele:\" data-toc-modified-id=\"Ihre-Lernziele:-1\"><span class=\"toc-item-num\">1 </span>Ihre Lernziele:</a></span></li><li><span><a href=\"#Exkurs:-Was-mir-an-Python-gefällt\" data-toc-modified-id=\"Exkurs:-Was-mir-an-Python-gefällt-2\"><span class=\"toc-item-num\">2 </span>Exkurs: Was mir an Python gefällt</a></span></li><li><span><a href=\"#Rückgabewerte\" data-toc-modified-id=\"Rückgabewerte-3\"><span class=\"toc-item-num\">3 </span>Rückgabewerte</a></span></li><li><span><a href=\"#Schrittweise-Entwicklung\" data-toc-modified-id=\"Schrittweise-Entwicklung-4\"><span class=\"toc-item-num\">4 </span>Schrittweise Entwicklung</a></span></li><li><span><a href=\"#Komposition\" data-toc-modified-id=\"Komposition-5\"><span class=\"toc-item-num\">5 </span>Komposition</a></span></li><li><span><a href=\"#Boolesche-Funktionen\" data-toc-modified-id=\"Boolesche-Funktionen-6\"><span class=\"toc-item-num\">6 </span>Boolesche Funktionen</a></span></li><li><span><a href=\"#Noch-mehr-Rekursion\" data-toc-modified-id=\"Noch-mehr-Rekursion-7\"><span class=\"toc-item-num\">7 </span>Noch mehr Rekursion</a></span></li><li><span><a href=\"#Vertrauensvorschuss\" data-toc-modified-id=\"Vertrauensvorschuss-8\"><span class=\"toc-item-num\">8 </span>Vertrauensvorschuss</a></span></li><li><span><a href=\"#Ein-weiteres-Beispiel\" data-toc-modified-id=\"Ein-weiteres-Beispiel-9\"><span class=\"toc-item-num\">9 </span>Ein weiteres Beispiel</a></span></li><li><span><a href=\"#Typen-prüfen\" data-toc-modified-id=\"Typen-prüfen-10\"><span class=\"toc-item-num\">10 </span>Typen prüfen</a></span></li><li><span><a href=\"#Debugging\" data-toc-modified-id=\"Debugging-11\"><span class=\"toc-item-num\">11 </span>Debugging</a></span></li><li><span><a href=\"#Glossar\" data-toc-modified-id=\"Glossar-12\"><span class=\"toc-item-num\">12 </span>Glossar</a></span></li><li><span><a href=\"#Übung\" data-toc-modified-id=\"Übung-13\"><span class=\"toc-item-num\">13 </span>Übung</a></span><ul class=\"toc-item\"><li><span><a href=\"#Aufgabe-1\" data-toc-modified-id=\"Aufgabe-1-13.1\"><span class=\"toc-item-num\">13.1 </span>Aufgabe 1</a></span></li><li><span><a href=\"#Aufgabe-2\" data-toc-modified-id=\"Aufgabe-2-13.2\"><span class=\"toc-item-num\">13.2 </span>Aufgabe 2</a></span></li><li><span><a href=\"#Aufgabe-3\" data-toc-modified-id=\"Aufgabe-3-13.3\"><span class=\"toc-item-num\">13.3 </span>Aufgabe 3</a></span></li><li><span><a href=\"#Aufgabe-4\" data-toc-modified-id=\"Aufgabe-4-13.4\"><span class=\"toc-item-num\">13.4 </span>Aufgabe 4</a></span></li><li><span><a href=\"#Aufgabe-5\" data-toc-modified-id=\"Aufgabe-5-13.5\"><span class=\"toc-item-num\">13.5 </span>Aufgabe 5</a></span></li></ul></li></ul></div>"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## 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",
"Das Modul os stellt Funktionen bereit, um Funktionalitäten des Betriebssystems zu nutzen. Beispielsweise können wir damit Verzeichnis-Inhalte auflisten, durch Verzeichnisse navigieren, Informationen zu Dateien bekommen und Dateieigenschaften verändern. Das folgende Programm gibt eine Liste aller Jupyter-Notebooks im aktuellen Verzeichnis zusammen mit der Dateigröße aus und berechnet die Gesamtgröße der Dateien:"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# Tabellenkopf ausgeben\n",
"print(\"Bytes\\tName\")\n",
"print(\"--------------------------------------------------------------\")\n",
"# Inhalt des aktuellen Verzeichnisses durchlaufen\n",
"for entry in os.scandir():\n",
" if entry.is_file() and entry.name.endswith(\".ipynb\"):\n",
" size = entry.stat().st_size\n",
" bytes_sum +=size\n",
" print(\"{:5d}\".format(size), entry.name, sep='\\t')\n",
" \n",
"print(\"--------------------------------------------------------------\")\n",
"print(bytes_sum, \"bytes =\", bytes_sum/1000, \"kilobytes =\", bytes_sum/1000000, \"Megabytes\")"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Der Aufruf einer Funktion erzeugt einen Rückgabewert, den wir üblicherweise einer Variable zuweisen oder als Teil eines Ausdrucks verwenden:\n",
"\n",
"```python\n",
"height = radius * math.sin(radians)\n",
"```\n",
"\n",
"Die (meisten) Funktionen, die wir bisher geschrieben haben sind \"leer\" - sie haben keinen Rückgabewert. Präziser ausgedrückt ist ihr Rückgabewert `None` (also nichts).\n",
"\n",
"In diesem Kapitel schreiben wir (endlich) ertragreiche Funktionen. Damit haben wir bereits kurz in [Seminar 3](notebooks_seminar03.ipynb) angefangen und lernen jetzt genauer, wie das funktioniert. Das erste Beispiel ist die Funktion `kreisflaeche`, die die Fläche eines Kreises für einen gegebenen Radius berechnet:"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
" a = math.pi * radius**2\n",
" return a"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Wir haben die `return`-Anweisung vorher schon einmal gesehen, aber in ertragreichen Funktionen folgt hinter der `return`-Anweisung ein Ausdruck (im Beispiel oben `a`). Die Anweisung bedeutet: \"Beende sofort diese Funktion und verwende den folgenden Ausdruck als Rückgabewert.\" Der Ausdruck kann beliebig kompliziert sein, wir könnten diese Funktion also auch kürzer schreiben: "
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
" return math.pi * radius**2"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Auf der anderen Seite können uns **temporäre Variablen** wie `a` beim Debugging helfen.\n",
"\n",
"Manchmal ist es nützlich, mehrere `return`-Anweisungen zu haben - eine in jedem Zweig einer Verzweigung:"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"def betrag(x):\n",
" if x < 0:\n",
" return -x\n",
" else:\n",
" return x"
]
},
{
"cell_type": "markdown",
"metadata": {},
"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",
"\n",
"\n",
"In einer eintragreichen Funktion sollten wir sicherstellen, dass jeder mögliche Pfad durch den Code eine `return`-Anweisung erreicht. Zum Beispiel:"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"def betrag(x):\n",
" if x < 0:\n",
" return -x\n",
" if x > 0:\n",
" return x"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"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:"
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"print(betrag(0))"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Übrigens, Python bietet eine eingebaute Funktion `abs` die den Betrag einer Zahl berechnet:"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"print(abs(-42))\n",
"print(abs(0))"
]
},
{
"cell_type": "markdown",
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
"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:\n",
"\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",
"Schauen Sie sich den Aufgabentext genau an und formulieren Sie den Kopf der Funktion. Wie soll die Funktion heißen und welche Parameter müssen Sie der Funktion übergeben? Stellen Sie sicher, dass Sie die Definition mit `def` starten, die Parameter in Klammern setzten und am Ende der Zeile einen Doppelpunkt setzen. \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",
"Wenn Sie 3 verschiedene Werte zurückgeben wollen, wie viele Bedingungen müssen Sie dann prüfen? Sie können `elif` verwenden um nach der ersten `if` Bedingung weitere einzufügen. \n",
" \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",
"Die Prüfung braucht insgesamt 3 Zweige, die erste Bedingung ist: `if x>y:`. Wie müssen die anderen Bedingungen formuliert werden?\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",
"Stellen Sie sicher, dass jede Eingabe zu einem Ergebnis führt. Prüfen Sie Ihre Funktion mit verschiedenen Werten für `x` und `y`.\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",
"Falls die Rückgabe nicht funktioniert, prüfen Sie zunächst folgendes: Haben Sie alles richtig geschrieben? Haben Sie alle nötigen Doppelpunkte und Klammern gesetzt? Haben Sie alles korrekt eingerückt? Enthält jeder der Zweige eine `return` Anweisung?\n",
" \n",
" </div> \n",
"</details>"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# Implementieren Sie hier die Funktion compare"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Wenn Sie größere Funktionen schreiben, kann es sein, dass Sie mehr Zeit mit der Fehlersuche (Debugging) verbringen.\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",
"Nehmen wir z.B. an, dass wir die Entfernung zwischen zwei Punkten berechnen wollen, die durch die Koordinaten $(x_1, y_1)$ und $(x_2, x_2)$ gegeben sind. Nach dem [Satz des Pythagoras](https://de.wikipedia.org/wiki/Satz_des_Pythagoras) ist die Entfernung: \n",
"\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",
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
"\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",
"Mit dieser Information können wir sofort eine Skizze der Funktion schreiben:"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"def entfernung(x1, y1, x2, y2):\n",
" return 0.0"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Ganz offensichtlich berechnet diese Variante nicht die Entfernung, sie liefert stets Null zurück. Aber sie ist syntaktisch korrekt und sie läuft, das heißt, wir können die Funktion testen, bevor wir sie verkomplizieren.\n",
"\n",
"Rufen Sie die Funktion mit Beispielargumenten auf, um sie zu testen: "
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"entfernung(1, 2, 4, 6)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Diese Werte sind so gewählt, dass die horizontale Distanz drei ist und die vertikale Distanz 4 - dadurch ist das Ergebnis 5 - die Hypothenuse eines Dreiecks mit den Seitenlängen 3-4-5. Wenn wir die Funktion testen ist es hilfreich, das richtige Ergebnis zu kennen.\n",
"\n",
" \n",
"\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. 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: "
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"def entfernung(x1, y1, x2, y2):\n",
" dx = x2 - x1\n",
" dy = y2 - y1\n",
" print('dx ist', dx)\n",
" print('dy ist', dy)\n",
" return 0.0\n",
"\n",
"entfernung(1, 2, 4, 6)"
]
},
{
"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 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`:"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"def entfernung(x1, y1, x2, y2):\n",
" dx = x2 - x1\n",
" dy = y2 - y1\n",
" dquadrat = dx**2 + dy**2\n",
" print('dquadrat ist: ', dquadrat)\n",
" return 0.0\n",
"\n",
"entfernung(1, 2, 4, 6)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"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:"
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"import math\n",
"def entfernung(x1, y1, x2, y2):\n",
" dx = x2 - x1\n",
" dy = y2 - y1\n",
" dquadrat = dx**2 + dy**2\n",
" ergebnis = math.sqrt(dquadrat)\n",
" return ergebnis\n",
"\n",
"entfernung(1, 2, 4, 6)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Falls das richtig funktioniert, sind wir fertig. Ansonsten könnten wir beispielsweise den Wert von `ergebnis` vor der `return`-Anweisung mit `print` ausgeben.\n",
"\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 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 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",
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
"**Übung:** Nutzen Sie das Prinzip der schrittweisen Entwicklung, um eine Funktion `hypothenuse` zu schreiben, die die Länge der Hypothenuse eines [rechtwinkligen Dreiecks](https://de.wikipedia.org/wiki/Rechtwinkliges_Dreieck) 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).\n",
"\n",
"<details>\n",
" <summary type=\"button\" class=\"btn btn-primary\">1. Hinweis</summary>\n",
" <div class=\"alert alert-info\" role=\"alert\">\n",
" \n",
" Wie immer fangen wir damit an, den Kopf der Funktion zu schreiben. Denken Sie darüber nach, welche Parameter laut Aufgabenstellung benötigt werden. Da wir eine Funktion mit Rückgabewert schreiben möchten, muss in Ihrem Code eine `return`-Anweisung auftauchen. Welchen Wert möchten Sie zurückgeben? Fügen Sie den entsprechenden Platzhalter ein. \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",
"Wir können die Hypothenuse mit dem Satz des Pythagoras berechnen. Suchen Sie die Formel heraus und berechnen Sie das erste Zwischenergebnis.\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",
"Berechnen Sie zuerst $c^2$, indem Sie $a^2$ und $b^2$ addieren und schreiben Sie eine `print`-Anweisung, die das Zwischenergebnis ausgibt. Führen Sie die Funktion mit Werten aus, bei denen Sie das Ergebnis überprüfen können. Wie funktionieren Potenzen nochmal in Python? Das haben Sie bereits gelernt. \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",
"Die Funktion `math.sqrt()` berechnet die Quadratwurzel. Verwenden Sie diese Funktion, um die Wurzel von $c^2$ zu erhalten. Das Ergebnis ist die Länge der Seite c (der Hypothenuse). Vergessen Sie nicht, das `math` Modul zu importieren. Überprüfen Sie, ob Sie das richtige Ergebnis für c erhalten.\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",
"Jetzt sollte die Funktion korrekt funktionieren. Tauschen Sie den Rückgabewertplatzhalter mit dem korrekten Rückgabewert und entfernen Sie alle `print`-Anweisungen oder kommentieren Sie diese aus.\n",
" </div> \n",
"</details>"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"\n",
"\n",
"([Nina Owens](http://geometry157.blogspot.de/2013/06/types-of-triangles-there-are-several.html))"
]
},
{
"cell_type": "markdown",
"metadata": {},
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
"\n",
"Wie Sie mittlerweile wissen sollten, können wir eine Funktion innerhalb einer anderen aufrufen. Als Beispiel werden wir eine Funktion schreiben, die zwei Punkte erwartet - den Mittelpunkt eines Kreises und einen Punkt auf dem Kreisumfang - und uns daraus die Fläche des Kreises berechnet.\n",
"\n",
"Angenommen, die Koordinaten des Mittelpunktes sind in den Variablen `xc` und `yc` gespeichert und die des Punktes auf dem Kreisumfang in `xp` und `yp`. Der erste Schritt ist, den Radius des Kreises zu berechnen, der sich aus der Entfernung der beiden Punkte ergibt. Wir haben gerade eine Funktion `entfernung` geschrieben, die das erledigt: "
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"radius = entfernung(xc, yc, xp, yp)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Der nächste Schritt ist, die Fläche eines Kreises mit diesem Radius zu berechnen. Das haben wir auch schon implementiert:"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"ergebnis = kreisflaeche(radius)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Wenn wir diese Schritte in einer Funktion verkapseln, erhalten wir:"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"def kreisflaeche_2(xc, yc, xp, yp):\n",
" radius = entfernung(xc, yc, xp, yp)\n",
" ergebnis = kreisflaeche(radius)\n",
" return ergebnis"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Die Hilfsvariablen `radius` und `ergebnis` sind hilfreich für Entwicklung und Debugging, aber sobald das Programm funktioniert können wir es kompakter aufschreiben durch die **Komposition** von Funktionsaufrufen:"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"def kreisflaeche_2(xc, yc, xp, yp):\n",
" return kreisflaeche(entfernung(xc, yc, xp, yp))"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
""
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Funktionen können Boolesche Werte zurückliefern. Das ist praktkisch, um komplizierte Tests in einer Funktion zu verstecken. Zum Beispiel: "
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"def ist_teilbar(x, y):\n",
" if x % y == 0:\n",
" return True\n",
" else:\n",
" return False"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Es ist üblich, Booleschen Funktionen Namen zu geben, die wie Ja-/Nein-Fragen klingen; `ist_teilbar` gibt entweder `True` oder `False` zurück und zeigt damit an, ob `x` durch `y` teilbar ist.\n",
"\n",
"Hier ist ein Beispiel:"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"ist_teilbar(6, 4)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"ist_teilbar(6, 3)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Das Ergebnis des `==`-Operators ist ein Boolescher Wert, daher können wir die Funktion kompakter aufschreiben, indem wir den Wert direkt zurückgeben:"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"def ist_teilbar(x, y):\n",
" return x % y == 0"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Boolesche Funktionen werden oft in Verzweigungen genutzt:"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"if ist_teilbar(x, 2):\n",
" print('x ist eine gerade Zahl')"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Es mag verlockend erscheinen, stattdessen folgendes zu schreiben:"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"if ist_teilbar(x, 2) == True:\n",
" print('x ist eine gerade Zahl')"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Aber dieser zusätzliche Vergleich ist unnötig.\n",
"\n",
"Schreiben Sie als Übung eine Funktion `ist_zwischen(x, y, z)`, die `True` zurückgibt, wenn $x \\le y \\le z$ gilt und ansonsten `False`."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"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",
"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",
"**vorpal:** Ein Adjektiv welches genutzt wird, um etwas zu beschreiben, was vorpal ist.\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",
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
"\n",
"\\begin{align}\n",
"0! &= 1\\\\\n",
"n! &= n(n-1)!\n",
"\\end{align}\n",
"\n",
"Diese Definition sagt aus, dass die Fakultät von 0 gleich 1 ist und die Fakultät jedes anderen Wertes $n$ entspricht $n$ multipliziert mit der Fakultät von $n-1$.\n",
"\n",
"Also ist 3! gleich 3 mal 2!, was 2 mal 1! ist, was 1 mal 0! ist. Zusammengenommen ist 3! also gleich 3 mal 2 mal 1 mal 1 - also 6.\n",
"\n",
"Wenn wir etwas rekursiv definieren können, dann können wir auch eine Python-Funktion schreiben, um das ganze auszuwerten. Der erste Schritt ist, zu entscheiden, was die Parameter sein sollen. In diesem Beispiel sollte es klar sein, dass `fakultaet` eine ganze Zahl erwartet: \n",
"\n",
"```python\n",
"def fakultaet(n):\n",
"```"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Wenn das Argument 0 übergeben wird, müssen wir einfach nur 1 zurückgeben:"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"def fakultaet(n):\n",
" if n == 0:\n",
" return 1"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Ansonsten, und das ist der spannende Teil, müssen wir einen rekursiven Aufruf machen, um die Fakultät von $n-1$ zu berechnen und dann mit $n$ zu multiplizieren:"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"def fakultaet(n):\n",
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
" return 1\n",
" else:\n",
" rekursion = fakultaet(n-1)\n",
" ergebnis = n * rekursion\n",
" return ergebnis\n",
"\n",
"fakultaet(3)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Der Kontrollfluss dieses Programms ist ähnlich dem von `countdown` in [Abschnitt 5.8](seminar05.ipynb#5.8-Rekursion). Wenn wir `fakultaet` mit dem Wert 3 aufrufen, passiert folgendes:\n",
"\n",
"Da 3 ungleich 0 ist, führen wir den zweiten Zweig aus und berechnen die Fakultät von $n-1$ ...\n",
"- Da 2 ungleich 0 ist, führen wir den zweiten Zweig aus und berechnen die Fakultät von $n-1$ ...\n",
" - Da 1 ungleich 0 ist, führen wir den zweiten Zweig aus und berechnen die Fakultät von $n-1$ ...\n",
" - Da 0 gleich 0 ist, führen wir den ersten Zweig aus und geben 1 zurück, ohne weitere rekursive Aufrufe zu tätigen.\n",
" \n",
" Der Rückgabewert, 1, wird mit n multipliziert, was 1 ist, und das Ergebnis zurückgegeben.\n",
" \n",
" Der Rückgabewert, 1, wird mit n multipliziert, was 2 ist, und das Ergebnis zurückgegeben.\n",
" \n",
"Der Rückgabewert, 2, wird mit n multipliziert, was 3 ist, und das Ergebnis 6 wird zum Rückgabewert des Funktionsaufrufs, der den ganzen Vorgang gestartet hat.\n",
"\n",
"Die folgende Abbildung zeigt wie das Stapeldiagramm für diese Folge von Funktionsaufrufen aussieht:\n",
"\n",
"\n",
"\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.\n",
"\n",
"Nutzen Sie auch [http://pythontutor.com/](http://pythontutor.com/visualize.html#code=def%20fakultaet%28n%29%3A%0A%20%20%20%20if%20n%20%3D%3D%200%3A%0A%20%20%20%20%20%20%20%20return%201%0A%20%20%20%20else%3A%0A%20%20%20%20%20%20%20%20rekursion%20%3D%20fakultaet%28n-1%29%0A%20%20%20%20%20%20%20%20ergebnis%20%3D%20n%20*%20rekursion%0A%20%20%20%20%20%20%20%20return%20ergebnis%0A%0Afakultaet%283%29&cumulative=false&curInstr=0&heapPrimitives=nevernest&mode=display&origin=opt-frontend.js&py=3&rawInputLstJSON=%5B%5D&textReferences=false), um die einzelnen Schritte nachzuvollziehen!"
{
"cell_type": "markdown",
"metadata": {},
"source": [
"\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",
"Das gleiche gilt, wenn wir eine unserer eigenen Funktionen aufrufen. Beispielsweise haben wir in [Abschnitt 6](#6-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 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. "
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
"\n",
"Neben der Fakultät ist ein weiteres übliches Beispiel für eine rekursiv definierte mathematische Funktion die [Fibonacci-Folge](https://de.wikipedia.org/wiki/Fibonacci-Folge):\n",
"\n",
"\\begin{align}\n",
"fibonacci(0) &= 0\\\\\n",
"fibonacci(1) &= 1\\\\\n",
"fibonacci(n) &= fibonacci(n-1) + fibonacci(n-2)\\\\\n",
"\\end{align}\n",
"\n",
"Übersetzt nach Python schaut das so aus:"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"def fibonacci(n):\n",
" if n == 0:\n",
" return 0\n",
" elif n == 1:\n",
" return 1\n",
" else:\n",
" return fibonacci(n-1) + fibonacci(n-2)\n",
"\n",
"fibonacci(7)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Wenn Sie hier versuchen, dem Kontrollfluss zu folgen, wird - selbst für kleine Werte von $n$ - ihr Kopf explodieren. Aber mit Vertrauensvorschuss - wenn wir annehmen dass die zwei rekursiven Aufrufe korrekt funktionieren - wird klar, dass wir das richtige Ergebnis durch Addition der Werte erhalten.\n",
"\n",
"**Übung:** Probierem Sie aus, bis zu welchem Wert von $n$ Sie die Funktion noch aufrufen können, ohne zu lange warten zu müssen. Wenn Sie wollen, können Sie auch `print`-Ausgaben zur Funktion hinzufügen, um den Ablauf nachzuverfolgen (`print(' '*n, n)` erzeugt beispielsweise eine übersichtliche Ausgabe). Rufen Sie die Funktion dann aber besser mit sehr kleinen Werten für `n` auf.\n",
"\n",
"\n",
"\n",
"([Borb](https://commons.wikimedia.org/wiki/File:FibonacciBlocks.svg))"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
"\n",
"Was passiert, wenn wir `fakultaet` mit dem Wert `1.5` als Argument aufrufen?"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"fakultaet(1.5)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Das sieht nach einer unendlichen Rekursion aus. Aber wie kann das sein? Die Funktion hat doch einen Basisfall - wenn `n == 0` ist. \n",
"\n",
"\n",
"\n",
"Nun, wenn `n` keine ganze Zahl ist, können wir den Basisfall *verpassen* und eine unendliche Rekursion wird durchgeführt.\n",
"\n",
"Im ersten rekursiven Aufruf ist der Wert von `n` gleich 0.5. Im nächsten ist der Wert `-0.5`. Ab da wird der Wert immer kleiner (immer negativer) aber er wird nie gleich `0` sein. \n",
"\n",
"Wir haben zwei Optionen, dieses Problem zu beheben:\n",
"\n",
"1. Wir können versuchen, die Funktion `fakultaet` zu verallgemeinern, so dass sie auch mit Gleitkommazahlen arbeitet.\n",
"2. Wir können `fakultaet` anpassen, so dass der Typ des übergebenen Arguments geprüft wird.\n",
"\n",
"Die erste Option nennt sich [Gamma-Funktion](https://de.wikipedia.org/wiki/Gammafunktion) und sprengt den Rahmen dieses Kurses. Also schauen wir uns die zweite Option an.\n",
"\n",
"Mit Hilfe der eingebauten Funktion `isinstance` können wir den Typ des Arguments prüfen. Und wenn wir schon einmal dabei sind, können wir auch gleich sicherstellen, dass das Argument positiv ist: "
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"def fakultaet(n):\n",
" if not isinstance(n, int):\n",
" print('Die Fakultät ist nur für ganze Zahlen definiert.')\n",
" return None\n",
" elif n < 0:\n",
" print('Die Fakultät für negative ganze Zahlen ist nicht definiert.')\n",
" return None\n",
" elif n == 0:\n",
" return 1\n",
" else:\n",
" return n * fakultaet(n-1)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Der erste Basisfall behandelt Zahlen die keine ganzen Zahlen sind; der zweite behandelt negative ganze Zahlen. In beiden Fällen gibt die Funktion eine Fehlermeldung aus und gibt `None` zurück, um anzuzeigen, dass etwas schiefgelaufen ist:"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"print(fakultaet(\"fred\"))"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"print(fakultaet(-2))"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"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",
"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",
"Im [Notebook 11 Abschnitt 4](seminar11.ipynb#reverse-lookup) werden wir eine flexiblere Alternative kennenlernen, um eine Fehlermeldung auszugeben: Ausnahmebehandlung."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"\n",
"Ein großes Programm in kleinere Funktionen zu zerlegen erzeugt ganz natürliche Kontrollpunkte. Wenn eine Funktion nicht funktioniert, gibt es drei Möglichkeiten, die wir in Betracht ziehen sollten:\n",
"\n",
"1. Es stimmt etwas nicht mit den Argumenten der Funktion; eine Vorbedingung ist verletzt.\n",
"2. Es stimmt etwas nicht mit der Funktion; eine Nachbedingung ist verletzt.\n",
"3. Es stimmt etwas nicht mit dem Rückgabewert der Funktion oder der Art und Weise, wie dieser verwendet wird.\n",
"\n",
"Um die erste Möglichkeit auszuschließen, können wir `print`-Anweisungen am Anfang der Funktion einfügen und die Werte der Parameter (und vielleicht deren Typ) ausgeben. Oder wir können Code einfügen, der die Vorbedingungen explizit prüft (wie wir es bei `fakultaet` gerade eben gemacht haben).\n",
"\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 4](#4-Schrittweise-Entwicklung)). \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:"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"def fakultaet(n):\n",
" space = ' ' * (4 * n)\n",
" print(space, 'fakultaet', n)\n",
" if n == 0:\n",
" print(space, 'returning 1')\n",
" return 1\n",
" else:\n",
" rekursion = fakultaet(n-1)\n",
" ergebnis = n * rekursion\n",
" print(space, 'returning', ergebnis)\n",
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Dabei ist `space` eine Zeichenkette voller Leerzeichen, die die Einrückung der Ausgabe kontrolliert. Probieren Sie es aus:"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"fakultaet(4)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Wenn Sie der Kontrollfluss verwirrt, dann kann diese Art der Ausgabe hilfreich sein. Es braucht etwas Zeit, guten Hilfscode zu entwickeln, aber etwas Hilfscode kann uns viel Zeit beim Debuggen ersparen.\n",
"\n",
"\n",
"\n",
"([Debugging](https://xkcd.com/1722/), Randall Munroe) [Erklärung des Comics](https://www.explainxkcd.com/wiki/index.php/1722:_Debugging) falls Sie mehr lernen wollen."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Legen wir uns eine Liste mit den wichtigsten Begriffen an, die wir im Kapitel 6 gelernt haben:\n",
"\n",
"- temporäre Variable:\n",
"- toter Code:\n",
"- schrittweise Entwicklung\n",
"- Hilfscode:\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."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Zeichnen Sie (mit Bleistift und Papier) ein Stapeldiagramm für das folgende Programm wenn bei der Ausführung die Zeile `x = x + 1` in der Funktion `a` erreicht wurde. Was gibt das Programm aus?\n"
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"def b(z):\n",
" prod = a(z, z)\n",
" print(z, prod)\n",
" return prod\n",
"\n",
"def a(x, y):\n",
" x = x + 1\n",
" return x * y\n",
"\n",
"def c(x, y, z):\n",
" total = x + y + z\n",
" square = b(total)**2\n",
" return square\n",
"\n",
"x = 1\n",
"y = x + 1\n",
"print(c(x, y+3, x+y))"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"\n",
"Die [Ackermannfunktion](https://de.wikipedia.org/wiki/Ackermannfunktion), $A(m, n)$ ist folgendermaßen definiert:\n",
"\n",
"\\begin{equation}\n",
"A(m,n) = \n",
"\\begin{cases}\n",
"n+1 & \\ \\ \\text{falls}\\ m=0\\\\\n",
"A(m-1, 1) & \\ \\ \\text{falls}\\ m > 0\\ \\text{und}\\ n = 0\\\\\n",
"A(m-1, A(m, n-1)) & \\ \\ \\text{falls}\\ m > 0\\ \\text{und}\\ n > 0\n",
"\\end{cases}\n",
"\\end{equation}\n",
"\n",
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
"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`?\n",
"\n",
"In den folgenden Hinweisen werden wir die Funktion schrittweise entwickeln. Versuchen Sie, die Aufgabe in Partnerarbeit zu lösen und verwenden Sie so wenige Hinweise 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 zuerst den Kopf der Funktion und überlegen Sie sich, wieviele Zweige Ihre Funktion benötigt. Schreiben Sie die entsprechende Anzahl an `if`-Bedingungen (bzw. `elif`-Bedingungen) und `return`-Anweisungen. Keine Sorge, Sie müssen sich an dieser Stelle noch keine Gedanken machen, wie die `if`-Bedingungen aussehen werden.\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",
"Im nächsten Schritt werden wir die korrekten `if`-Bedinungen schreiben. Die geschweifte Klammer in der mathematischen Notation unterscheidet die unterschiedlichen Fälle, die bei bestimmten Bedingungen eintreten. Die Bedingungen stehen hinter dem Schlüsselwort \"falls\". Übersetzen Sie diese Formel in Python. Fügen Sie in jedem Zweig eine `print`-Anweisung ein, die den Zweig eindeutig identifiziert. Wir werden diese Hilfsanweisung später löschen. Testen Sie jetzt, ob Ihre Funktion für verschiedene Eingabewerte den korrekten Zweig ansteuert. Testen Sie auch die Möglichkeiten `m=0` und `n=0`. Im nächsten Hinweis (2.5) finden Sie die verschiedenen `if`-Bedingungen.\n",
" </div> \n",
"</details> \n",
" \n",
"\n",
"<details>\n",
" <summary type=\"button\" class=\"btn btn-primary\">2.5. Hinweis</summary>\n",
" <div class=\"alert alert-info\" role=\"alert\">\n",
"\n",
"Die `if`-Bedingungen sind: `if m==0:`für den Basisfall, `elif n==0 and m>0:` und `elif m>0 and n>0:`\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",
"Im nächsten Schritt werden wir den korrekten Rückgabewert für den Basisfall eingeben. Anschließend testen Sie Ihre Funktion mit einem Aufruf des Basisfalls. Schauen Sie sich an, wie die Anweisung für die Berechnung des Basisfalls lauten muss. Sie können die korrekte Anweisung in Hinweis 3.5 nachlesen, wenn Sie sich unsicher sind oder Ihr Code nicht korrekt funktioniert.\n",
" \n",
" </div> \n",
"</details>\n",
"\n",
"<details>\n",
" <summary type=\"button\" class=\"btn btn-primary\">3.5. Hinweis</summary>\n",
" <div class=\"alert alert-info\" role=\"alert\">\n",
"\n",
"Die Rückgabe im Basisfall muss `return n+1` lauten. \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",
"Anschließened beschäftigen wir mit der Bedingung `n==0`. Schauen Sie sich auch hier wieder die Formel an und versuchen Sie herauszufinden, welche Werte Sie beim rekursiven Aufruf übergeben müssen. In 4.5 können Sie den korrekten Aufruf nachlesen, wenn Sie dabei Hilfe brauchen. \n",
" \n",
" </div> \n",
"</details>\n",
"\n",
"<details>\n",
" <summary type=\"button\" class=\"btn btn-primary\">4.5. Hinweis</summary>\n",
" <div class=\"alert alert-info\" role=\"alert\">\n",
" \n",
"Die korrekte Anweisung lautet: `return ack (m-1,1)` \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",
"Der letzte Zweig wird aufgerufen, wenn sowohl m als auch n größer als Null sind. Schauen Sie sich genau an, wie die Funktion dabei definiert ist. Dabei fällt auf, dass die Funktion sich selbst im Funktionsaufruf rekursiv aufruft. Schreiben Sie diesen Funktionsaufruf und achten Sie auf die richtige Klammersetzung. Wenn Sie Hilfe brauchen, können Sie sich den korrekten Aufruf in 5.5 anschauen, versuchen Sie es aber zunächst einmal selber und testen Sie, ob bei Ihrem Aufruf das korrekte Ergebnis erscheint. \n",
" </div> \n",
"</details>\n",
"\n",
"<details>\n",
" <summary type=\"button\" class=\"btn btn-primary\">5.5 Hinweis</summary>\n",
" <div class=\"alert alert-info\" role=\"alert\">\n",
" \n",
"Die korrekte `return`-Anweisung lautet: `return ack (m-1, ack(m, n-1))` \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",
"Jetzt funktioniert Ihr Code wie gewünscht. An dieser Stelle ist es sinnvoll, Wächter und einen Doc-String einzubauen. Überlegen Sie dabei, für welche Zahlenbereiche die Funktion definiert ist und stellen Sie sicher, dass ihr Code das zu Beginn überprüft. Schreiben Sie auch einen Doc-String, der beschreibt, welche Eingaben die Funktion akzeptiert und was Sie als Ergebnis ausgibt.\n",
" </div> \n",
"</details>\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# Implementieren Sie hier die Ackermannfunktion\n",
"\n",
"\n",
"# Testaufruf\n",
"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",
"Es folgt der Code, der Mithilfe der Hinweise entwickelt wurde. Wenn Ihr Code etwas anders aussieht, ist das selbstverständlich okay, solange er das korrekte Ergebnis berechnet.\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"def ack(m,n):\n",
" \n",
" '''Die Funktion erwartet 2 positive ganze Zahlen m und n und berechnet daraus die Ackermannfunktion.\n",
" Werden ungültige Werte eingegeben, ist der Rückgabewert None und ein Fehler wird ausgegeben, anderfalls wird\n",
" das Ergebnis zurückgegeben.'''\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": {},
"source": [
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
"\n",
"Ein [Palindrom](https://de.wikipedia.org/wiki/Palindrom) ist ein Wort, welches vorwärts und rückwärts gelesen gleich ist. Beispielsweise \"neben\" oder \"hangnah\" (wenn wir Großschreibung ignorieren, gibt es auch Substantive, z.B. \"Reliefpfeiler\" oder \"Anna\"). Rekursiv definiert, ist ein Wort ein Palindrom, wenn der erste und letzte Buchstabe identisch sind und der Mittelteil ein Palindrom ist.\n",
"\n",
"Die folgenden Funktionen erwarten eine Zeichenkette als Argument und geben die ersten, letzten und mittleren Buchstaben zurück:"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"def first(word):\n",
" return word[0]\n",
"\n",
"def last(word):\n",
" return word[-1]\n",
"\n",
"def middle(word):\n",
" return word[1:-1]"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Wir werden in [Seminar 8](seminar08.ipynb) sehen, wie sie funktionieren.\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?"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# Testen Sie hier die Funktionen first, last und middle "
]
},
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
{
"cell_type": "markdown",
"metadata": {},
"source": [
"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",
"\n",
"Wie gehabt, folgen jetzt einige Hinweise, versuchen Sie zunächst die Aufgaben in Partnerarbeit zu lösen und verwenden Sie so wenige von den Hinweisen wie möglich, das ist die beste Übung:\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",
"Wir werden uns die Funktionen `first, last` und `middle` zu nutze machen um die Funktion zu schreiben.\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",
"Schreiben Sie zunächst den Kopf der Funktion. welche Zustände muss die Funktion zurückgeben? Schreiben Sie auch die entsprechenden Rückgabeeingaben, machen Sie sich (noch) keinen Kopf darüber, wie sie entscheiden, welcher Wert zurückgegeben wird. Wenn Sie die Funktion jetzt testen wird immer der erste Rückgabewert, den Sie eingegeben haben zurückgegeben. \n",
" </div> \n",
"</details> \n",
" \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",
"Wenn wir automatisch ein Palindrom testen wollen, vergleichen wir immer den ersten mit dem letzen Buchstaben, diese müssen gleich sein. Das bedeutet, das der hier geschriebene Test bei Palindromen mit unterschiedlich gesetzen Leerzeichen kein Palindrom findet. \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",
" Wir werden die Funktion rekursiv implementieren, wir müssen uns also den Basisfall eines Palindroms überlegen. Wie könnte dieser Basisfall aussehen? \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",
"Der Basisfall für ein Palindrom ist eine Zeichenkette mit nur einem oder keinem Zeichen ist immer ein Palindrom. Für diesen Wert gibt die Funktion den Wert `True` zurück. Verwenden Sie die Funktion `len` um die Länge der Zeichenkette zu testen und schreiben Sie die entsprechende `if`-Bedingung.\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",
"Was muss die Abbruchbedingung sein, wenn die Funktion `False` zurückgeben soll?\n",
" </div> \n",
"</details>\n",
"\n",
"<details>\n",
" <summary type=\"button\" class=\"btn btn-primary\">7. Hinweis</summary>\n",
" <div class=\"alert alert-info\" role=\"alert\">\n",
"\n",
"Verwenden Sie einen Test auf Ungleichheit. Da in einem Palindrom der erste und letzte Buchstabe gleich sein müssen, geben Sie `False` zurück, wenn der Rückgabewert von `first` und `last` nicht gleich ist. \n",
" \n",
" </div> \n",
"</details>\n",
"\n",
"<details>\n",
" <summary type=\"button\" class=\"btn btn-primary\">8. Hinweis</summary>\n",
" <div class=\"alert alert-info\" role=\"alert\">\n",
" \n",
"Abschließend müssen die noch den Rekursiven Funktionsaufruf schreiben. Überlegen Sie, wie Sie die Funktion mit dem zweiten bis vorletzten Buchstaben aufrufen können. \n",
" \n",
" </div> \n",
"</details>\n",
"<details>\n",
" <summary type=\"button\" class=\"btn btn-primary\">9. Hinweis</summary>\n",
" <div class=\"alert alert-info\" role=\"alert\">\n",
" \n",
"Der rekursive Funktionsaufruf muss `ist_palindrom(middle(s))` lauten\n",
" </div> \n",
"</details>\n"
]
},
{
"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",
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"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\")"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
" \n",
"\n",
"([Farrar, Straus and Giroux](http://time.com/3771063/mark-saltveit-world-palindrome-championship/))\n",
"\n",
1389
1390
1391
1392
1393
1394
1395
1396
1397
1398
1399
1400
1401
1402
1403
1404
1405
1406
1407
1408
1409
1410
1411
1412
1413
1414
1415
1416
1417
1418
1419
1420
1421
1422
1423
1424
1425
1426
1427
1428
1429
1430
1431
1432
1433
1434
1435
1436
"### Aufgabe 4\n",
"\n",
"Eine Zahl $a$ ist eine Potenz von $b$, wenn $a$ durch $b$ teilbar ist und $a/b$ eine Potenz von $b$ ist. (Beispielsweise ist 27 eine Potenz von 3, denn 27 ist durch 3 teilbar und 9 ist eine Potenz von 3.) Schreiben Sie eine Funktion `ist_potenz` die Parameter `a` und `b` erwartet und `True` zurückgibt, wenn `a` eine Potenz von `b` ist (ansonsten `False`). \n",
"<details>\n",
" <summary type=\"button\" class=\"btn btn-info\">Hinweis</summary>\n",
" <div class=\"alert alert-info\" role=\"alert\">\n",
"\n",
"Überlegen Sie sich, was der Basisfall ist und wie Sie diesen behandeln.\n",
" \n",
" </div> \n",
"</details>\n",
"\n",
"Es folgen wie immer Hinweise zum Lösen der Aufgabe. Versuchen Sie zunächst, die Aufgabe in Partnerarbeit zu lösen, so lernen Sie immer am besten. \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 zunächst wie üblich den Kopf der Funktion inklusive der Parameter und schreiben Sie auch bereits die `return`-Anweisungen für die möglichen Ergebnisse der Funktion. Wir kümmern uns anschließend darum, wie diese Werte jeweils erreicht werden.\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",
"Wir müssen uns die beiden Basisfälle überlegen. Diese sind die Fälle, bei denen die Funktion `True` zurückgibt. Die Basisfälle sind `a==b` und `b==1`. Machen Sie sich klar, warum diese Fälle die Basisfälle sind.\n",
" </div> \n",
"</details> \n",
" \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",
"Überlegen Sie nun, wie Sie testen können, ob eine Zahl die Potenz einer anderen Zahl ist. Verwenden Sie den `modulo` Operator.\n",
"\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",
1438
1439
1440
1441
1442
1443
1444
1445
1446
1447
1448
1449
1450
1451
1452
1453
1454
1455
1456
1457
1458
1459
"Damit eine Zahl eine Potenz einer anderen Zahl sein kann, muss Sie restlos durch die andere Zahl teilbar sein. Implementieren Sie die Verzweigung und schreiben Sie den Zweig, der `False` zurück gibt. Das ist der Zweig, der ausgeführt wird, wenn alle anderen Bedingungen nicht erfüllt werden.\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",
"Wenn sich $a$ restlos durch $b$ teilen lässt, muss sich in diesem Zweig die Funktion mit einer `return`-Anweisung rekursiv aufrufen. Überlegen Sie sich, welche Werte für $a$ und $b$ übergeben werden müssen.\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",
"Für $a$ müssen Sie $a/b$ übergeben, da Sie ja prüfen müssen, ob $a/b$ auch restlos durch $b$ teilbar ist. Für $b$ müssen Sie daher wieder $b$ übergeben.\n",
"\n",
" </div> \n",
"</details>"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# 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",
]
},
{
"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": {},
"source": [
"\n",
"Der [größte gemeinsame Teiler](https://de.wikipedia.org/wiki/Gr%C3%B6%C3%9Fter_gemeinsamer_Teiler) (ggT) von $a$ und $b$ ist die größte Zahl die beide Zahlen ($a$ und $b$) ohne Rest teilt.\n",
"\n",
"Eine Möglichkeit den ggT zweier Zahlen zu berechnen, beruht auf der Beobachtung, dass, wenn $r$ der Rest der Division von $a$ durch $b$ ist, dann $ggT(a,b) = ggT(b,r)$ gilt. Als Basisfall können wir $ggT(a,0)=a$ nutzen.\n",
"\n",
1507
1508
1509
1510
1511
1512
1513
1514
1515
1516
1517
1518
1519
1520
1521
1522
1523
1524
1525
1526
1527
1528
1529
1530
1531
1532
1533
1534
1535
1536
1537
1538
1539
1540
1541
1542
1543
1544
1545
1546
1547
1548
1549
"Schreiben Sie eine Funktion `ggt`, die zwei Parameter `a` und `b` erwartet und den größten gemeinsamen Teiler zurückgibt. Bedenken Sie, dass wir für die Berechnung anders vorgehen als bei der Verwendung des Euklidischen Algorithmus. Nutzen Sie die Hinweise, wenn Sie Hilfe benötigen. \n",
"\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 zunächst den Kopf der Funktion und die `return`-Anweisung. Lassen Sie die Funktion zuerst einen Platzhalter zurückgeben, wir werden uns in späteren Schritten damit beschäftigen, das korrekte Ergebnis auszugeben.\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",
"Wie lautet der Basisfall? Schreiben Sie die `if`-Anweisung, die prüft, ob der Basisfall wahr ist. Passen Sie die `return`-Anweisung so an, dass das für den Basisfall passende Ergebnis zurückgegeben wird. Testen Sie, ob dies funktioniert, indem Sie die Funktion mit dem Basisfall aufrufen und sich das Ergebnis anschauen.\n",
"\n",
" </div> \n",
"</details> \n",
" \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",
"Da wir eine rekursive Funktion implementieren wollen, müssen wir uns überlegen, wie wir den rekursiven Aufruf gestalten können. Schauen Sie sich die Aufgabenstellung an und überlegen Sie, welche Argumente Sie dem Aufruf übergeben. \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",
"\n",
"Wir wollen für b den Rest aus der Divison von a und b übergeben. Berechnen Sie den Wert. Sie können direkt die Berechnung übergeben oder das Ergebnis in einer Variablen speichern und diese Variable übergeben. Für a übergeben wir b aus diesem Funktionsaufruf.\n",
" \n",
" </div> \n",
"</details>"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# Implementieren Sie hier die Funktion ggt\n"
]
},
{
"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",
]
},
{
"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",
{
"cell_type": "markdown",
"metadata": {},
"source": [
" Speichern Sie dieses Notebook, so dass Ihre Änderungen nicht verlorengehen (nicht auf einem Pool-Rechner). Klicken Sie dazu oben links auf das Disketten-Icon und nutzen Sie beispielsweise einen USB-Stick, E-Mail, Google Drive, Dropbox oder Ihre [HU-Box](https://box.hu-berlin.de/). "
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"\n",
"Herzlichen Glückwunsch! Sie haben das 6. Kapitel geschafft. Weiter geht es in [7: Iteration](seminar07.ipynb)."