Skip to content
Snippets Groups Projects
seminar06.ipynb 44.1 KiB
Newer Older
{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Seminar Problemorientierte Programmierung\n",
    "\n",
    "## Exkurs: Was mir an Python gefällt\n",
    "\n",
Prof. Dr. Robert Jäschke's avatar
Prof. Dr. Robert Jäschke committed
    "In dieser Rubrik, die immer am Anfang eines Kapitels steht, möchte ich Ihnen zeigen, wofür ich Python nutze und warum ich es mag. Sie werden vielleicht noch nicht verstehen, was ich genau mache, aber Sie sehen damit schon einmal die Möglichkeiten von Python und können später darauf zurückgreifen. Da dies auch ein Exkurs ist, können Sie diese Rubrik gerne auch erst einmal überspringen.\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
Prof. Dr. Robert Jäschke's avatar
Prof. Dr. Robert Jäschke committed
    "\"\"\" \n",
    "Quelle: https://teampython.wordpress.com/2015/12/12/print-a-christmas-tree/\n",
    "Python 3 version by antiloquax (2015), based on code from datamungeblog.com.\n",
    "\"\"\"\n",
    " \n",
    "from random import choice\n",
    "from random import random\n",
    " \n",
    "# If you change this, use an odd number.\n",
    "size = 21\n",
    "\n",
    "# Probability that a character will be green.\n",
    "prob_gr = 0.6\n",
    "# Colour codes.\n",
    "colours = [31, 33, 34, 35, 36, 37]\n",
    "# Characters to use for decorations. Experiment with these.\n",
    "# The chr(169) and chr(174) characters may not work in all terminals\n",
    "# (extended ASCII, c and r in a circle).\n",
    "decs = ['@', '&', '*', chr(169), chr(174)]\n",
    "\n",
    "# Format string for printing blinking characters.\n",
    "blink_col = \"\\033[5;{0}m{1}\\033[0m\"\n",
    "# String to print a green octothorpe ('#').\n",
    "leaf = \"\\033[32m#\\033[0m\"\n",
    "\n",
    "# Width of the tree, will grow by 2 each time.\n",
    "width = 1\n",
    "# Initialise the tree string, with a star at the top.\n",
    "tree = \"\\n{}*\\n\".format(' ' * (size))\n",
    "\n",
    "\"\"\" Main Loop starts now.\"\"\"\n",
    " \n",
    "\"\"\" We can't use the normal \"format\" centering approach:\n",
    "    (\"{:^nn}\".format(string) where \"nn\" is the width of the line), \n",
    "    with these ansi codes. This is because Python sees the strings as being\n",
    "    more than one character long (15 & 10 for baubles and leaves).\"\"\"\n",
    "\n",
    "# Loop from (size - 1) down to 0, using the counter as the padding size.\n",
    "for pad in range(size - 1, -1, -1):\n",
    "    # Increase the width of the tree by 2.\n",
    "    width += 2\n",
    "     \n",
    "    # Put the characters for the line in \"temp\".\n",
    "    temp = \"\"\n",
    "    for j in range(width):\n",
    "        # Make some leaves.\n",
    "        if random() < prob_gr:\n",
    "            temp += leaf\n",
    "        # And also some baubles.\n",
    "        else:\n",
    "            temp += blink_col.format(choice(colours), choice(decs))\n",
    "\n",
    "    # Add that string to the line, with padding.\n",
    "    tree += \"{0}{1}\\n\".format(' ' * pad, temp)\n",
    "\n",
    "# Add a \"trunk\" of 2 lines and return.\n",
    "print(tree + \"{0}{1}\\n\".format(' ' * (size - 1), \"000\") * 2)\n",
    "print(\"\\x46\\x72\\x6f\\x68\\x65\\x20\\x46\\x65\\x73\\x74\\x74\\x61\\x67\\x65\\x21\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Und noch viele weitere schöne Beispiele: https://codegolf.stackexchange.com/questions/15860/"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 6 Ertragreiche Funktionen\n",
    "\n",
    "Viele Python-Funktionen die wir bis jetzt genutzt haben, wie z.B. die Mathematik-Funktionen aus dem `math`-Modul, erzeugen Rückgabewerte (*return values*). Aber die meisten Funktionen die wir bisher selber geschrieben haben sind \"leer\": sie bewirken etwas, beispielsweise die Ausgabe eines Wertes (mit Hilfe der `print`-Funktion) oder die Bewegung einer Schildkröte, aber sie haben keinen Rückgabewert. In diesem Kapitel werden wir lernen, wie wir \"ertragreiche Funktionen\", also solche mit Rückgabewert, schreiben können.\n",
    "\n",
    "![RFC 1149.5 specifies 4 as the standard IEEE-vetted random number.](https://imgs.xkcd.com/comics/random_number.png)\n",
    "\n",
    "([Random Number](https://xkcd.com/221/), Randall Munroe)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 6.1 Rückgabewerte\n",
    "\n",
Prof. Dr. Robert Jäschke's avatar
Prof. Dr. Robert Jäschke committed
    "Der Aufruf einer Funktion erzeugt einen Rückgabewert, den wir üblicherweise einer Variable zuweisen oder als Teil eines Ausdrucks verwenden:\n",
    "\n",
    "```python\n",
    "e = math.exp(1.0)\n",
Prof. Dr. Robert Jäschke's avatar
Prof. Dr. Robert Jäschke committed
    "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",
Prof. Dr. Robert Jäschke's avatar
Prof. Dr. Robert Jäschke committed
    "In diesem Kapitel schreiben wir (endlich) ertragreiche Funktionen. 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": [
Prof. Dr. Robert Jäschke's avatar
Prof. Dr. Robert Jäschke committed
    "def kreisflaeche(radius):\n",
    "    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": [
Prof. Dr. Robert Jäschke's avatar
Prof. Dr. Robert Jäschke committed
    "def kreisflaeche(radius):\n",
    "    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",
Prof. Dr. Robert Jäschke's avatar
Prof. Dr. Robert Jäschke committed
    "![dead code](https://amor.cms.hu-berlin.de/~jaeschkr/teaching/spp/deadcode.svg)\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, denn wenn `x` gleich 0 ist, ist keine der beiden Bedingungen erfüllt und die Funktion wird beendet, ohne dass eine `return`-Anweisung erreicht wird. Wenn die Ausführung das Ende einer Funktion erreicht, ist der Rückgabewert `None`, was nicht der Betrag von 0 ist:"
   ]
  },
Prof. Dr. Robert Jäschke's avatar
Prof. Dr. Robert Jäschke committed
  {
   "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",
   "metadata": {},
   "source": [
Prof. Dr. Robert Jäschke's avatar
Prof. Dr. Robert Jäschke committed
    "Schreiben Sie eine Funktion `compare`, die zwei Parameter `x` und `y` erwartet und `1` zurückliefert, wenn `x > y` ist, `0` wenn `x == y` gilt und `-1` für `x < y`:"
Prof. Dr. Robert Jäschke's avatar
Prof. Dr. Robert Jäschke committed
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Implementieren Sie hier die Funktion compare"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 6.2 Schrittweise Entwicklung\n",
    "\n",
    "Wenn Sie größere Funktionen schreiben kann es sein, dass Sie mehr Zeit mit der Fehlersuche (Debugging) verbringen.\n",
    "\n",
Prof. Dr. Robert Jäschke's avatar
Prof. Dr. Robert Jäschke committed
    "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",
Prof. Dr. Robert Jäschke's avatar
Prof. Dr. Robert Jäschke committed
    "\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",
    "\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",
    "![Satz des Pythagoras](https://upload.wikimedia.org/wikipedia/commons/d/d1/01-Rechtwinkliges_Dreieck-Pythagoras.svg) \n",
    "\n",
    "([Petrus3743](https://commons.wikimedia.org/wiki/File:01-Rechtwinkliges_Dreieck-Pythagoras.svg), Wikimedia Commons)\n",
    "\n",
Prof. Dr. Robert Jäschke's avatar
Prof. Dr. Robert Jäschke committed
    "An dieser Stelle haben wir uns davon überzeugt, dass die Funktion syntaktisch korrekt ist und wir können damit beginnen, Code zum Rumpf hinzuzufügen. Ein naheliegender nächster Schritt ist, die Differenzen $x_2-x_1$ und $y_2-y_1$ zu berechnen. Die nächste Version speichert die Werte in temporären Variablen und gibt sie aus: "
Prof. Dr. Robert Jäschke's avatar
Prof. Dr. Robert Jäschke committed
   ]
  },
  {
   "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 erste Berechnung korrekt durchgeführt wurde. Falls nicht, gibt es nur wenige Zeilen, 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). Schließlich können wir die Funktion `math.sqrt` nutzen um das Ergebnis zu berechnen und zurückzugeben:"
   ]
  },
  {
   "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. In jedem Fall kann Ihnen schrittweise Entwicklung viel Zeit bei der Fehlersuche ersparen.\n",
    "\n",
    "Die wichtigsten Punkte dieses Vorgehens sind:\n",
    "1. Beginnen Sie mit einem funktionierenden Programm und führen Sie nur kleine, inkrementelle Änderungen durch. Sollte ein Fehler auftreten, so sollten Sie zu jedem Zeitpunkt eine gute Idee davon haben, wodurch er hervorgerufen wird.\n",
    "2. Nutzen Sie Variablen, um Zwischenwerte zu speichern, so dass Sie diese ausgeben (`print`) und überprüfen können.\n",
    "3. Sobald das Programm funktioniert sollten Sie Teile des Hilfscodes entfernen oder mehrere Anweisungen zu einer Verbundanweisung zusammenfügen, aber nur, wenn sich dadurch die Lesbarkeit des Programms nicht verschlechtert.\n",
    "\n",
    "**Übung:** Nutzen Sie das Prinzip der schrittweisen Entwicklung, um eine Funktion `hypothenuse` zu schreiben, die die Länge der Hypothenuse eines rechtwinkligen Dreiecks zurückgibt, wenn die Längen der beiden Katheden gegeben sind. Dokumentieren Sie jeden Entwicklungsschritt hier im Notebook (d.h., erzeugen Sie eine Kopie der Funktion, bevor Sie den nächsten Entwicklungsschritt durchführen)."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# beginnen Sie hier mit der Entwicklung der Funktion"
   ]
  },
Prof. Dr. Robert Jäschke's avatar
Prof. Dr. Robert Jäschke committed
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "![Right Triangle](http://www.mezzacotta.net/owls/comics/0336.png)\n",
    "\n",
    "([Nina Owens](http://geometry157.blogspot.de/2013/06/types-of-triangles-there-are-several.html))"
   ]
  },
Prof. Dr. Robert Jäschke's avatar
Prof. Dr. Robert Jäschke committed
  {
   "cell_type": "markdown",
   "metadata": {},
Prof. Dr. Robert Jäschke's avatar
Prof. Dr. Robert Jäschke committed
   "source": [
    "### 6.3 Komposition\n",
    "\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": [
Prof. Dr. Robert Jäschke's avatar
Prof. Dr. Robert Jäschke committed
    "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:"
Prof. Dr. Robert Jäschke's avatar
Prof. Dr. Robert Jäschke committed
   ]
  },
  {
   "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": [
    "![Kreisfläche](https://upload.wikimedia.org/wikipedia/commons/d/d2/Circle_Area_de.svg)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 6.4 Boolesche Funktionen\n",
    "\n",
Prof. Dr. Robert Jäschke's avatar
Prof. Dr. Robert Jäschke committed
    "Funktionen können Boolesche Werte zurückliefern. Das ist praktkisch, um  komplizierte Tests in einer Funktion zu verstecken. Zum Beispiel: "
Prof. Dr. Robert Jäschke's avatar
Prof. Dr. Robert Jäschke committed
   ]
  },
  {
   "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": [
Prof. Dr. Robert Jäschke's avatar
Prof. Dr. Robert Jäschke committed
    "# implementieren Sie hier die Funktion"
Prof. Dr. Robert Jäschke's avatar
Prof. Dr. Robert Jäschke committed
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 6.5 Noch mehr Rekursion\n",
    "\n",
    "Wir haben bisher nur eine kleine Teilmenge von Python kennengelernt aber vielleicht interessiert es Sie zu wissen, dass diese Teilmenge eine *komplette* Programmiersprache darstellt. Das heißt, alles was berechnet werden kann, können wir mit den bisher erlernten Anweisungen und Funktionen ausdrücken! Jedes jemals geschriebene Programm könnten wir umschreiben, so dass es nur mit den Sprachmerkmalen auskommt, die wir bis jetzt gelernt haben (gut, wir bräuchten noch ein paar Anweisungen um Geräte wie z.B. die Maus, Festplatten, etc. zu kontrollieren).\n",
    "\n",
    "![Alan Turing](https://upload.wikimedia.org/wikipedia/commons/thumb/7/79/Alan_Turing_az_1930-as_%C3%A9vekben.jpg/372px-Alan_Turing_az_1930-as_%C3%A9vekben.jpg)\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",
    "\n",
    "Um einen Idee davon zu bekommen, was wir mit den Werkzeugen, die wir bisher kennengelernt haben, schon erreichen können, wollen wir einige rekursiv definierte mathematische Funktionen implementieren. Eine rekursive Definition ist ähnlich einer [zirkulären Definition](https://en.wikipedia.org/wiki/Circular_definition) (*circular definition* - leider konnte ich dafür keinen deutschen Begriff finden) in dem Sinne, dass die Definition eine Referenz auf das was definiert wird enthält. Eine richtig zirkuläre Definition ist nicht sehr nützlich:\n",
    "\n",
Prof. Dr. Robert Jäschke's avatar
Prof. Dr. Robert Jäschke committed
    "**vorpal:** Ein Adjektiv welches genutzt wird, um etwas zu beschreiben, was vorpal ist.\n",
Prof. Dr. Robert Jäschke's avatar
Prof. Dr. Robert Jäschke committed
    "\n",
    "Wenn Sie so eine Definition in einem Wörterbuch sehen, sind sie vermutlich verärgert. Andererseits, wenn wir uns die Definition der Fakultätsfunktion heraussuchen (die mit dem Symbol ! bezeichnet wird), finden wir vermutlich etwas in der Art:\n",
    "\n",
    "\\begin{align}\n",
    "0! &= 1\\\\\n",
    "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",
    "    if n == 1:\n",
    "        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",
    "![Stapeldiagramm](http://amor.cms.hu-berlin.de/~jaeschkr/teaching/spp/stapeldiagramm_fakultaet.svg)\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."
Prof. Dr. Robert Jäschke's avatar
Prof. Dr. Robert Jäschke committed
   ]
Prof. Dr. Robert Jäschke's avatar
Prof. Dr. Robert Jäschke committed
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 733 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 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 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 839 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 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 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 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 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 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 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
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 6.6 Vertrauensvorschuss\n",
    "\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",
    "\n",
    "Das gleiche gilt wenn wir eine unserer eigenen Funktionen aufrufen. Beispielsweise haben wir in [Abschnitt 6.4](#6.4-Boolesche-Funktionen) eine Funktion `ist_teilbar` geschrieben, die bestimmt, ob eine Zahl durch eine andere teilbar ist. Sobald wir uns davon überzeugt haben, dass diese Funktion korrekt arbeitet - durch Verstehen des Codes und Testen - können wir die Funktion nutzen, ohne uns den Rumpf noch einmal anzuschauen.\n",
    "\n",
    "Das gleiche gilt für rekursive Programme. Wenn wir auf einen rekursiven Funktionsaufruf treffen, können wir, anstatt dem Kontrollfluss zu folgen, annehmen, das der rekursive Aufruf funktioniert (also den richtigen Wert zurückliefert) und uns selbst beispielsweise fragen \"Angenommen, ich kann die Fakultät von $n-1$ berechnen, kann ich dann die Fakultät von $n$ berechnen?\" Das funktioniert offensichtlich - indem wir mit $n$ multiplizieren.\n",
    "\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": [
    "### 6.7 Ein weiteres Beispiel\n",
    "\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",
    "![Fibonacci-Folge](https://upload.wikimedia.org/wikipedia/commons/9/95/FibonacciBlocks.svg)\n",
    "\n",
    "([Borb](https://commons.wikimedia.org/wiki/File:FibonacciBlocks.svg))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 6.8 Typen prüfen\n",
    "\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",
    "![What the heck?](http://s2.quickmeme.com/img/02/0213043c2680cd6a7dad73df5359c75ce089262b3b86476a62e6564f54c40c08.jpg)\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 ist (oder Null). Damit können wir zeigen, dass die Rekursion terminiert.\n",
    "\n",
    "Dieses Programm demonstriert ein Entwurfsmuster welches manchmal **Wächter** (*guardian*) genannt wird. Die ersten beiden Verzweigungen agieren als Wächter, die den darauffolgenden Code vor Werten beschützen, die Fehler hervorrufen könnten. Die Wächter ermöglichen uns, die Korrektheit des Codes zu beweisen.\n",
    "\n",
    "Im [Abschnitt 11.4](seminar11.ipynb#reverse-lookup) werden wir eine flexiblere Alternative kennenlernen, um eine Fehlermeldung auszugeben: Ausnahmebehandlung."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 6.9 Debugging\n",
    "\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 6.2](#6.2-Schrittweise-Entwicklung)). \n",
    "\n",
    "Wenn die Funktion richtig arbeitet (oder es danach ausschaut), sollten wir uns die Stelle anschauen, wo die Funktion aufgerufen wird und sicherstellen, dass der Rückgabewert richtig verwendet wird (bzw. überhaupt verwendet wird!).\n",
    "\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",
    "        return ergebnis"
   ]
  },
  {
   "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",
    "![When you Google an error message and it gets no results, you can be pretty sure you've found a clue to the location of Martin's sword.](https://imgs.xkcd.com/comics/debugging_2x.png)\n",
    "\n",
    "([Debugging](https://xkcd.com/1722/), Randall Munroe)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 6.10 Glossar\n",
    "\n",
    "Legen wir uns eine Liste mit den wichtigsten Begriffen an, die wir im Kapitel 5 gelernt haben:\n",
    "\n",
    "- temporäre Variable:\n",
    "- toter Code:\n",
    "- schrittweise Entwicklung\n",
    "- Hilfscode:\n",
    "- Wächter:\n",
    "\n",
    "Ergänzen Sie die Liste in eigenen Worten. Das ist eine gute Erinnerungs- und Übungsmöglichkeit."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 6.11 Übung\n",
    "\n",
    "#### Aufgabe 1\n",
    "\n",
    "Zeichnen Sie (mit Bleistift und Papier) ein Stapeldiagramm für das folgende Programm. Was gibt das Programm aus?\n"
   ]
  },
  {
   "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": [
    "#### Aufgabe 2\n",
    "\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",
    "Schreiben Sie eine Funktion `ack` die die Ackermannfunktion berechnet. Berechnen Sie mit ihrer Funktion `ack(3,4)`, was 125 ergeben sollte. Was passiert für größere Werte von `m` und `n`? Lösung: http://thinkpython2.com/code/ackermann.py"
   ]
  },
  {
   "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": [
    "#### Aufgabe 3\n",
    "\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 [Kapitel 8](seminar08.ipynb) sehen, wie sie funktionieren.\n",
    "\n",
    "1. Testen Sie diese Funktionen. Was passiert, wenn Sie `middle` mit einer Zeichenkette mit nur zwei Zeichen aufrufen? Oder mit nur einem Zeichen? Was passiert mit der leeren Zeichenkette, geschrieben '', die keine Zeichen enthält?\n",
    "2. Schreiben Sie eine Funktion `ist_palindrom`, die eine Zeichenkette als Argument erwartet und `True` zurückliefert, wenn die Zeichenkette ein Palindrom ist und ansonsten `False`. (Erinnern Sie sich daran, dass Sie mit der eingebauten Funktion `len` die Länge einer Zeichenkette ermitteln können.)\n",
    "\n",
    "Lösung: http://thinkpython2.com/code/palindrome_soln.py"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Testen Sie hier die Funktionen first, last und middle \n",
    "# und implementieren Sie die Funktion ist_palindrom."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "![Palindrom](https://imagesvc.timeincapp.com/v3/mm/image?url=https%3A%2F%2Ftimedotcom.files.wordpress.com%2F2015%2F04%2Fgo-hang-a-salami.jpg&w=800&q=85)\n",
    "\n",
    "([Farrar, Straus and Giroux](http://time.com/3771063/mark-saltveit-world-palindrome-championship/))\n",
    "\n",
    "#### 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`). Hinweis: Überlegen Sie sich, was der Basisfall ist und wie Sie diesen behandeln."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Implementieren Sie hier die Funktion ist_potenz"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### Aufgabe 5\n",
    "\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",
    "Schreiben Sie eine Funktion `ggt`, die zwei Parameter `a` und `b` erwartet und den größten gemeinsamen Teiler zurückgibt."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Implementieren Sie hier die Funktion ggt\n"
   ]
  },
Prof. Dr. Robert Jäschke's avatar
Prof. Dr. Robert Jäschke committed
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "![Speichern](https://amor.cms.hu-berlin.de/~jaeschkr/teaching/spp/floppy.png) 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": [
Prof. Dr. Robert Jäschke's avatar
Prof. Dr. Robert Jäschke committed
    "![Smiley](https://upload.wikimedia.org/wikipedia/commons/7/70/Face-devil-grin.svg)\n",
Prof. Dr. Robert Jäschke's avatar
Prof. Dr. Robert Jäschke committed
    "\n",
Prof. Dr. Robert Jäschke's avatar
Prof. Dr. Robert Jäschke committed
    "Herzlichen Glückwunsch! Sie haben das 6. Kapitel geschafft. Weiter geht es in [7: Iteration](seminar07.ipynb)."
Prof. Dr. Robert Jäschke's avatar
Prof. Dr. Robert Jäschke committed
   ]
  }
 ],
 "metadata": {
  "language_info": {
   "name": "python",
   "pygments_lexer": "ipython3"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}