{ "cells": [ { "cell_type": "markdown", "id": "5705b2a6", "metadata": { "id": "5705b2a6", "tags": [] }, "source": [ "# Kapitel 11: Tupel\n", "[Chapter 11: Tuples](https://colab.research.google.com/github/AllenDowney/ThinkPython/blob/v3/chapters/chap11.ipynb)\n", "\n", "In diesem Kapitel wird ein weiterer eingebauter Datentyp, das Tupel, vorgestellt und gezeigt, wie Listen, assoziative Datenfelder und Tupel zusammenarbeiten.\n", "Außerdem beinhaltet das Kapitel Informationen zu Tupel-Zuweisungen und einer nützlichen Eigenschaft für Funktionen mit Argumentlisten variabler Länge: die Verpack- und Entpack-Operatoren.\n", "\n", "In den Übungen werden wir Tupel zusammen mit Listen und assoziativen Datenfeldern verwenden, um mehr Worträtsel zu lösen und effiziente Algorithmen zu implementieren.\n", "\n", "### Ihre Lernziele:\n", "\n", "Beschreiben Sie in 2-3 Stichpunkten kurz was Sie im Seminar heute lernen wollen. Klicken Sie dazu doppelt auf diesen Text und bearbeiten Sie dann den Text:\n", "\n", "- \n", "- \n", "- \n" ] }, { "cell_type": "markdown", "id": "9a0b03e4-0e53-40b4-8850-5fa03078f99a", "metadata": {}, "source": [ "## Herunterladen des unterstützenden Codes\n", "Die folgende Zelle lädt eine Datei herunter und führt einen Code aus, der speziell für dieses Notebook verwendet wird. Sie müssen diesen Code nicht verstehen, aber Sie sollten die Zelle vor allen weiteren Zellen in diesem Notebook ausführen:" ] }, { "cell_type": "code", "execution_count": null, "id": "295ac6d7", "metadata": { "id": "295ac6d7", "tags": [] }, "outputs": [], "source": [ "from os.path import basename, exists\n", "\n", "def download(url):\n", " filename = basename(url)\n", " if not exists(filename):\n", " from urllib.request import urlretrieve\n", "\n", " local, _ = urlretrieve(url, filename)\n", " print(\"Downloaded \" + str(local))\n", " return filename\n", "\n", "download('https://github.com/AllenDowney/ThinkPython/raw/v3/thinkpython.py');\n", "download('https://github.com/AllenDowney/ThinkPython/raw/v3/diagram.py');\n", "\n", "import thinkpython" ] }, { "cell_type": "markdown", "id": "19474596", "metadata": { "id": "19474596" }, "source": [ "## Tupel sind wie Listen\n", "\n", "Ein **Tupel** (Englisch: *tuple*) ist eine Abfolge von Werten. Diese Werte können jeden Datentypen haben und sind mit ganzen Zahlen indexiert, in dieser Hinsicht sind Tupel Listen also sehr ähnlich.\n", "Der wichtigste Unterschied zwischen den beiden ist aber, dass Tupel unveränderbar sind.\n", "\n", "Um ein Tupel zu erstellen können wir eine Liste mit Werten schreiben, die durch Kommata getrennt werden:" ] }, { "cell_type": "code", "execution_count": null, "id": "fb0bdca2", "metadata": { "id": "fb0bdca2" }, "outputs": [], "source": [ "t = 'l', 'u', 'p', 'i', 'n'\n", "type(t)" ] }, { "cell_type": "markdown", "id": "a2ec15d8", "metadata": { "id": "a2ec15d8" }, "source": [ "Auch wenn es nicht unbedingt notwendig ist, werden Tupel häufig in runden Klammern geschrieben:" ] }, { "cell_type": "code", "execution_count": null, "id": "5a6da881", "metadata": { "id": "5a6da881" }, "outputs": [], "source": [ "t = ('l', 'u', 'p', 'i', 'n')\n", "type(t)" ] }, { "cell_type": "markdown", "id": "9194a159", "metadata": { "id": "9194a159" }, "source": [ "Um ein Tupel mit einem einzigen Element zu erstellen, müssen wir es mit einem Komma am Ende schreiben:" ] }, { "cell_type": "code", "execution_count": null, "id": "e2596ca7", "metadata": { "id": "e2596ca7" }, "outputs": [], "source": [ "t1 = 'p',\n", "type(t1)" ] }, { "cell_type": "markdown", "id": "e39b95a5", "metadata": { "id": "e39b95a5" }, "source": [ "Ein einzelner Wert in Klammern ist kein Tupel:" ] }, { "cell_type": "code", "execution_count": null, "id": "a0d350a6", "metadata": { "id": "a0d350a6" }, "outputs": [], "source": [ "t2 = ('p')\n", "type(t2)" ] }, { "cell_type": "markdown", "id": "a64bfb64", "metadata": { "id": "a64bfb64" }, "source": [ "Ein anderer Weg, ein Tupel zu erstellen, ist die eingebaute Funktion `tuple`. Ohne Argument erstellt diese ein leeres Tupel:" ] }, { "cell_type": "code", "execution_count": null, "id": "c9100ee4", "metadata": { "id": "c9100ee4" }, "outputs": [], "source": [ "t = tuple()\n", "t" ] }, { "cell_type": "markdown", "id": "f3447831", "metadata": { "id": "f3447831" }, "source": [ "Wenn das Argument eine Folge ist (eine *Zeichenkette*, eine *Liste* oder ein *Tupel*), ist das Ergebnis ein Tupel mit den Elementen der Folge:" ] }, { "cell_type": "code", "execution_count": null, "id": "44bd3d83", "metadata": { "id": "44bd3d83" }, "outputs": [], "source": [ "t = tuple('lupin')\n", "t" ] }, { "cell_type": "markdown", "id": "2e48b980", "metadata": { "id": "2e48b980" }, "source": [ "Da `tuple` der Name einer eingebauten Funktion ist, sollten wir vermeiden, es als Variablennamen zu verwenden.\n", "\n", "Die meisten Operatoren für Listen funktionieren auch mit Tupeln.\n", "Der Klammer-Operator zum Beispiel indexiert ein Element:" ] }, { "cell_type": "code", "execution_count": null, "id": "92e55b2c", "metadata": { "id": "92e55b2c" }, "outputs": [], "source": [ "t[0]" ] }, { "cell_type": "markdown", "id": "2f702785", "metadata": { "id": "2f702785" }, "source": [ "Und der Slice-Operator wählt Elemente aus einem angegebenen Bereich aus:" ] }, { "cell_type": "code", "execution_count": null, "id": "38ee5c2a", "metadata": { "id": "38ee5c2a" }, "outputs": [], "source": [ "t[1:3]" ] }, { "cell_type": "markdown", "id": "c9ed9af2", "metadata": { "id": "c9ed9af2" }, "source": [ "Der `+`-Operator konkateniert Tupel:" ] }, { "cell_type": "code", "execution_count": null, "id": "2e0e311a", "metadata": { "id": "2e0e311a" }, "outputs": [], "source": [ "tuple('lup') + ('i', 'n')" ] }, { "cell_type": "markdown", "id": "1d7dcd6d", "metadata": { "id": "1d7dcd6d" }, "source": [ "Und der `*`-Operator dupliziert ein Tupel eine angegebene Anzahl von Malen:" ] }, { "cell_type": "code", "execution_count": null, "id": "8bb7d715", "metadata": { "id": "8bb7d715" }, "outputs": [], "source": [ "tuple('spam') * 2" ] }, { "cell_type": "markdown", "id": "a53ce8bd", "metadata": { "id": "a53ce8bd" }, "source": [ "Die `sorted`-Function funktioniert auch mit Tupeln -- aber das Ergebnis ist eine Liste, kein Tupel:" ] }, { "cell_type": "code", "execution_count": null, "id": "e653e00f", "metadata": { "id": "e653e00f" }, "outputs": [], "source": [ "sorted(t)" ] }, { "cell_type": "markdown", "id": "50e5cadc", "metadata": { "id": "50e5cadc" }, "source": [ "Die `reversed`-Function funktioniert ebenfalls mit Tupeln:" ] }, { "cell_type": "code", "execution_count": null, "id": "8969188d", "metadata": { "id": "8969188d" }, "outputs": [], "source": [ "reversed(t)" ] }, { "cell_type": "markdown", "id": "f6d973c5", "metadata": { "id": "f6d973c5" }, "source": [ "Das Ergebnis ist ein `reversed`-Object, das wir in eine Liste oder ein Tupel konvertieren können:" ] }, { "cell_type": "code", "execution_count": null, "id": "65d7ebaa", "metadata": { "id": "65d7ebaa" }, "outputs": [], "source": [ "tuple(reversed(t))" ] }, { "cell_type": "markdown", "id": "a7cb9ee6", "metadata": { "id": "a7cb9ee6" }, "source": [ "Ausgehend von den bisher gezeigten Beispielen könnte man annehmen, dass Tupel das Gleiche sind wie Listen." ] }, { "cell_type": "markdown", "id": "8c3f381e", "metadata": { "id": "8c3f381e" }, "source": [ "## Aber Tupel sind unveränderlich\n", "\n", "Wenn wir versuchen, ein Tupel mit dem Klammer-Operator zu modifizieren, erhalten wir einen `TypeError`:" ] }, { "cell_type": "code", "execution_count": null, "id": "b4970fe0", "metadata": { "id": "b4970fe0", "tags": [] }, "outputs": [], "source": [ "%%expect TypeError\n", "\n", "t[0] = 'L'" ] }, { "cell_type": "markdown", "id": "592ce99c", "metadata": { "id": "592ce99c" }, "source": [ "Und Tupel haben keine der Methoden, die verwendet werden um Listen zu verändern, wie etwa `append` und `remove`:" ] }, { "cell_type": "code", "execution_count": null, "id": "772738cc", "metadata": { "id": "772738cc", "tags": [] }, "outputs": [], "source": [ "%%expect AttributeError\n", "\n", "t.remove('l')" ] }, { "cell_type": "markdown", "id": "70772ba2", "metadata": { "id": "70772ba2" }, "source": [ "Erinnern Sie sich daran, dass ein \"Attribut\" eine Variable oder eine Methode ist, die in Zusammenhang mit einem Objekt steht -- diese Fehlermeldung bedeutet, dass Tupel keine Methode namens `remove` haben.\n", "\n", "Weil Tupel unveränderlich sind, sind sie hashbar, was bedeutet, dass sie als Schlüssel in einem assoziativen Datenfeld verwendet werden können.\n", "Das folgende assoziative Datenfeld enthält zum Beispiel zwei Tupel, die als Schlüssel auf ganze Zahlen verweisen." ] }, { "cell_type": "code", "execution_count": null, "id": "37e67042", "metadata": { "id": "37e67042" }, "outputs": [], "source": [ "d = {}\n", "d[1, 2] = 3\n", "d[3, 4] = 7" ] }, { "cell_type": "markdown", "id": "47ba17ab", "metadata": { "id": "47ba17ab" }, "source": [ "So können wir Tupel in einem assoziativen Datenfeld suchen:" ] }, { "cell_type": "code", "execution_count": null, "id": "d809a490", "metadata": { "id": "d809a490" }, "outputs": [], "source": [ "d[1, 2]" ] }, { "cell_type": "markdown", "id": "f2c0a354", "metadata": { "id": "f2c0a354" }, "source": [ "Oder wir können, wenn es eine Variable gibt, die sich auf das Tupel bezieht, diese als Schlüssel verwenden:" ] }, { "cell_type": "code", "execution_count": null, "id": "dfc42a8b", "metadata": { "id": "dfc42a8b" }, "outputs": [], "source": [ "t = (3, 4)\n", "d[t]" ] }, { "cell_type": "markdown", "id": "2ea8fc3c", "metadata": { "id": "2ea8fc3c" }, "source": [ "Tupel können auch als Werte in einem assoziativen Datenfeld vorkommen:" ] }, { "cell_type": "code", "execution_count": null, "id": "2debf30c", "metadata": { "id": "2debf30c" }, "outputs": [], "source": [ "t = tuple('abc')\n", "d = {'schluessel': t}\n", "d" ] }, { "cell_type": "markdown", "id": "25655ab3", "metadata": { "id": "25655ab3" }, "source": [ "## Tupel-Zuweisung\n", "\n", "Wir können ein Tupel mit Variablen auf die linke und ein Tupel mit Werten auf die rechte Seite einer Zuweisung schreiben:" ] }, { "cell_type": "code", "execution_count": null, "id": "1e94ea37", "metadata": { "id": "1e94ea37" }, "outputs": [], "source": [ "a, b = 1, 2" ] }, { "cell_type": "markdown", "id": "92c00ceb", "metadata": { "id": "92c00ceb" }, "source": [ "Die Werte werden den Variablen von links nach rechts zugewiesen -- in diesem Beispiel erhält `a` den Wert `1` und `b` den Wert `2`.\n", "Wir können so die Ergebnisse anzeigen:" ] }, { "cell_type": "code", "execution_count": null, "id": "99c96c7f", "metadata": { "id": "99c96c7f" }, "outputs": [], "source": [ "a, b" ] }, { "cell_type": "markdown", "id": "6362b36e", "metadata": { "id": "6362b36e" }, "source": [ "\n", "Etwas allgemeiner gesprochen: wenn die linke Seite einer Zuweisung ein Tupel ist, dann kann die rechte Seite jede Art von Folge sein -- *Zeichenkette*, *Liste* oder *Tupel*.\n", "\n", "Um eine email-Adresse in Username und Domain aufzuteilen können wir zum Beispiel schreiben:" ] }, { "cell_type": "code", "execution_count": null, "id": "b67881ed", "metadata": { "id": "b67881ed" }, "outputs": [], "source": [ "email = 'monty@python.org'\n", "username, domain = email.split('@')" ] }, { "cell_type": "markdown", "id": "d134a94c", "metadata": { "id": "d134a94c" }, "source": [ "Der Rückgabewert von `split` ist eine Liste mit zwei Elementen -- das erste Element wird ` username` zugewiesen, das zweite `domain`:" ] }, { "cell_type": "code", "execution_count": null, "id": "b4515e2b", "metadata": { "id": "b4515e2b" }, "outputs": [], "source": [ "username, domain" ] }, { "cell_type": "markdown", "id": "5a7e3c62", "metadata": { "id": "5a7e3c62" }, "source": [ "Die Anzahl der Variablen auf der linken und die Anzahl der Werte auf der rechten Seite müssen gleich sein -- andernfalls erhalten wir einen `ValueError`:" ] }, { "cell_type": "code", "execution_count": null, "id": "8e5b4a14", "metadata": { "id": "8e5b4a14", "tags": [] }, "outputs": [], "source": [ "%%expect ValueError\n", "\n", "a, b = 1, 2, 3" ] }, { "cell_type": "markdown", "id": "808c2928", "metadata": { "id": "808c2928" }, "source": [ "Tupel-Zuweisung ist praktisch für die Fälle in denen wir die Werte zweier Variablen tauschen möchten.\n", "Bei einer konventionellen Zuweisung müssten wir hierfür eine temporäre Variable verwenden, wie im folgenden Beispiel:" ] }, { "cell_type": "code", "execution_count": null, "id": "2389d6de", "metadata": { "id": "2389d6de" }, "outputs": [], "source": [ "temp = a\n", "a = b\n", "b = temp" ] }, { "cell_type": "markdown", "id": "98496d02", "metadata": { "id": "98496d02" }, "source": [ "Das funktioniert zwar, aber mit einer Tupel-Zuweisung können wir das Gleiche ohne die temporäre Variable tun:" ] }, { "cell_type": "code", "execution_count": null, "id": "5512edec", "metadata": { "id": "5512edec" }, "outputs": [], "source": [ "a, b = b, a" ] }, { "cell_type": "markdown", "id": "a66a87bc", "metadata": { "id": "a66a87bc" }, "source": [ "Das funktioniert, da alle Ausdrücke auf der rechten Seite vor jeglichen Zuweisungen ausgewertet werden.\n", "\n", "Wir können Tupel-Zuweisungen auch in `for`-Ausdrücken verwenden.\n", "Zum Beispiel können wir, um in einer Schleife durch die Elemente eines assoziativen Datenfeldes zu gehen, die `items`-Methode benutzen:" ] }, { "cell_type": "code", "execution_count": null, "id": "651ab417", "metadata": { "id": "651ab417" }, "outputs": [], "source": [ "d = {'eins': 1, 'zwei': 2}\n", "\n", "for item in d.items():\n", " schluessel, wert = item\n", " print(schluessel, '->', wert)" ] }, { "cell_type": "markdown", "id": "dd0d4feb", "metadata": { "id": "dd0d4feb" }, "source": [ "Bei jedem Durchgang der Schleife wird `item` ein Tupel zugewiesen, das einen *Schlüssel* und den korrespondierenden *Wert* enthält.\n", "\n", "Wir können diese Schleife auch etwas prägnanter schreiben:" ] }, { "cell_type": "code", "execution_count": null, "id": "2c0b7d47", "metadata": { "id": "2c0b7d47" }, "outputs": [], "source": [ "for schluessel, wert in d.items():\n", " print(schluessel, '->', wert)" ] }, { "cell_type": "markdown", "id": "f0513578", "metadata": { "id": "f0513578" }, "source": [ "Bei jedem Durchgang der Schleife werden ein Schlüssel und der korrespondierende Wert direkt `schluessel` und `wert` zugewiesen." ] }, { "cell_type": "markdown", "id": "efedeb37", "metadata": { "id": "efedeb37" }, "source": [ "## Tupel als Rückgabewerte\n", "\n", "Streng genommen kann eine Funktion nur einen Wert zurückgeben, wenn dieser Wert aber ein Tupel ist, hat dies den gleichen Effekt als würde man mehrere Werte zurückgeben.\n", "Wenn wir zum Beispiel zwei ganze Zahlen dividieren und den Quotienten sowie den Rest berechnen möchten, ist es ineffizient zuerst `x//y` und dann `x%y` zu berechnen. Es ist besser, beide gleichzeitig zu berechnen.\n", "\n", "Die eingebaute Funktion `divmod` nimmt zwei Argumente auf und gibt ein Tupel mit zwei Werten, dem *Quotienten* und dem *Rest* zurück:" ] }, { "cell_type": "code", "execution_count": null, "id": "fff80eaa", "metadata": { "id": "fff80eaa" }, "outputs": [], "source": [ "divmod(7, 3)" ] }, { "cell_type": "markdown", "id": "33f3c57d", "metadata": { "id": "33f3c57d" }, "source": [ "Wir können Tupel-Zuweisung nutzen, um die Elemente des Tupels in zwei Variablen zu speichern:" ] }, { "cell_type": "code", "execution_count": null, "id": "4a0eb2a9", "metadata": { "id": "4a0eb2a9" }, "outputs": [], "source": [ "quotient, rest = divmod(7, 3)\n", "quotient" ] }, { "cell_type": "code", "execution_count": null, "id": "d74ba1b6", "metadata": { "id": "d74ba1b6" }, "outputs": [], "source": [ "rest" ] }, { "cell_type": "markdown", "id": "15079805", "metadata": { "id": "15079805" }, "source": [ "Hier ist ein Beispiel für eine Funktion, die ein Tupel zurückgibt:" ] }, { "cell_type": "code", "execution_count": null, "id": "dad3b3bb", "metadata": { "id": "dad3b3bb" }, "outputs": [], "source": [ "def min_max(t):\n", " return min(t), max(t)" ] }, { "cell_type": "markdown", "id": "43c4e1e0", "metadata": { "id": "43c4e1e0" }, "source": [ "`max` und `min` sind eingebaute Funktionen, die das größte und das kleinste Element einer Folge finden.\n", "`min_max` berechnet beide und gibt ein Tupel mit zwei Werten zurück:" ] }, { "cell_type": "code", "execution_count": null, "id": "fbd90b0e", "metadata": { "id": "fbd90b0e" }, "outputs": [], "source": [ "min_max([2, 4, 1, 3])" ] }, { "cell_type": "markdown", "id": "86b60e71", "metadata": { "id": "86b60e71" }, "source": [ "So können wir die Ergebnisse Variablen zuweisen:" ] }, { "cell_type": "code", "execution_count": null, "id": "5a101efb", "metadata": { "id": "5a101efb" }, "outputs": [], "source": [ "niedrig, hoch = min_max([2, 4, 1, 3])\n", "niedrig, hoch" ] }, { "cell_type": "markdown", "id": "112b5aa2", "metadata": { "id": "112b5aa2", "tags": [] }, "source": [ "## Argumente verpacken\n", "\n", "Funktionen können eine variable Anzahl von Argumenten aufnehmen.\n", "Ein Parametername, der mit dem `*`-Operator beginnt, **verpackt** (Englisch: *packs*) die Argumente in ein Tupel.\n", "Die folgende Funktion nimmt zum Beispiel eine beliebige Zahl an Argumenten auf und berechnet deren arithmetisches Mittel -- also deren Summe, geteilt durch die Anzahl der Argumente:" ] }, { "cell_type": "code", "execution_count": null, "id": "0a33e2d0", "metadata": { "id": "0a33e2d0" }, "outputs": [], "source": [ "def mittel(*args):\n", " return sum(args) / len(args)" ] }, { "cell_type": "markdown", "id": "6044fc1b", "metadata": { "id": "6044fc1b" }, "source": [ "Der Parameter kann jeden beliebigen Namen haben, die übliche Benennung ist aber `args`.\n", "Wir können die Funktion so aufrufen:" ] }, { "cell_type": "code", "execution_count": null, "id": "336a08ca", "metadata": { "id": "336a08ca" }, "outputs": [], "source": [ "mittel(1, 2, 3)" ] }, { "cell_type": "markdown", "id": "a5e8b158", "metadata": { "id": "a5e8b158" }, "source": [ "Wenn wir eine Folge von Werten haben und diese als mehrere Argumente an eine Funktion übergeben wollen, können wir den `*`-Operator verwenden, um das Tupel zu **entpacken** (Englisch: *unpack*).\n", "`divmod` nimmt zum Beispiel exakt zwei Argumente auf -- wenn wir ein Tupel als Parameter übergeben erhalten wir eine Fehlermeldung:" ] }, { "cell_type": "code", "execution_count": null, "id": "991810bc", "metadata": { "id": "991810bc", "tags": [] }, "outputs": [], "source": [ "%%expect TypeError\n", "\n", "t = (7, 3)\n", "divmod(t)" ] }, { "cell_type": "markdown", "id": "5a9110db", "metadata": { "id": "5a9110db" }, "source": [ "Obwohl das Tupel zwei Elemente enthält, zählt es als ein einzelnes Argument.\n", "Aber wenn wir das Tupel entpacken, wird es behandelt als wären es zwei Argumente:" ] }, { "cell_type": "code", "execution_count": null, "id": "f25ebee1", "metadata": { "id": "f25ebee1" }, "outputs": [], "source": [ "divmod(*t)" ] }, { "cell_type": "markdown", "id": "da554863", "metadata": { "id": "da554863" }, "source": [ "Verpacken und entpacken können nützlich sein, wenn wir das Verhalten einer schon existierenden Funktion anpassen wollen.\n", "Diese Funktion nimmt zum Beispiel eine beliebige Anzahl von Argumenten auf, entfernt das niedrigste und das höchste und berechnet den Mittelwert vom Rest:" ] }, { "cell_type": "code", "execution_count": null, "id": "7ad64412", "metadata": { "id": "7ad64412" }, "outputs": [], "source": [ "def getrimmt_mittel(*args):\n", " niedrig, hoch = min_max(args)\n", " getrimmt = list(args)\n", " getrimmt.remove(niedrig)\n", " getrimmt.remove(hoch)\n", " return mittel(*getrimmt)" ] }, { "cell_type": "markdown", "id": "d1e05e49", "metadata": { "id": "d1e05e49" }, "source": [ "Zuerst verwendet sie `min_max`, um das niedrigste und das höchste Element zu finden.\n", "Danach konvertiert sie `args` zu einer Liste, damit die `remove`-Methode verwendet werden kann.\n", "Zuletzt entpackt sie die Liste, sodass die Elemente als separate Argumente und nicht als eine Liste an `mitte` weitergegeben werden.\n", "\n", "Hier ist ein Beispiel, das die Auswirkungen davon zeigt:" ] }, { "cell_type": "code", "execution_count": null, "id": "b2863701", "metadata": { "id": "b2863701" }, "outputs": [], "source": [ "mittel(1, 2, 3, 10)" ] }, { "cell_type": "code", "execution_count": null, "id": "cc1afa29", "metadata": { "id": "cc1afa29" }, "outputs": [], "source": [ "getrimmt_mittel(1, 2, 3, 10)" ] }, { "cell_type": "markdown", "id": "35e04996", "metadata": { "id": "35e04996" }, "source": [ "Diese Art von \"getrimmtem\" Mittelwert wird in manchen Sportarten mit subjektiver Bewertung -- zum Beispiel Tauchen oder Gymnastik -- verwendet, um die Auswirkungen eines einzelnen Wertungsrichters, dessen Bewertung sich sehr von denen der anderen unterscheidet zu reduzieren." ] }, { "cell_type": "markdown", "id": "c4572cd2", "metadata": { "id": "c4572cd2" }, "source": [ "## Zip\n", "\n", "Tupel sind praktisch, um in einer Schleife durch die Elemente zweier Folgen zu gehen und Operationen an den korrespondierenden Elementen durchzuführen.\n", "Nehmen wir zum Beispiel an, zwei Mannschaften spielen eine Serie von sieben Spielen und wir tragen die Ergebnisse in zwei Listen ein, eine pro Team:" ] }, { "cell_type": "code", "execution_count": null, "id": "ad3e6f81", "metadata": { "id": "ad3e6f81" }, "outputs": [], "source": [ "ergebnisse1 = [1, 2, 4, 5, 1, 5, 2]\n", "ergebnisse2 = [5, 5, 2, 2, 5, 2, 3]" ] }, { "cell_type": "markdown", "id": "b44f228b", "metadata": { "id": "b44f228b" }, "source": [ "Lasst uns nun überprüfen, wie viele Spiele jede Mannschaft gewonnen hat.\n", "Dafür werden wir `zip` verwenden, eine eingebaute Funktion, die zwei oder mehr Folgen aufnimmt und ein **Zip-Objekt** zurückgibt, das so genannt wird, weil es die Elemente der Folgen wie die Zähnchen eines Reißverschlusses (Englisch: *zipper*) verpaart:" ] }, { "cell_type": "code", "execution_count": null, "id": "9ce313ce", "metadata": { "id": "9ce313ce" }, "outputs": [], "source": [ "zip(ergebnisse1, ergebnisse2)" ] }, { "cell_type": "markdown", "id": "9adcf8f9", "metadata": { "id": "9adcf8f9" }, "source": [ "Wir können das Zip-Objekt verwenden, um in einer Schleife paarweise durch die Werte der Folgen zu gehen:" ] }, { "cell_type": "code", "execution_count": null, "id": "321d9c30", "metadata": { "id": "321d9c30" }, "outputs": [], "source": [ "for paar in zip(ergebnisse1, ergebnisse2):\n", " print(paar)" ] }, { "cell_type": "markdown", "id": "51d1dabb", "metadata": { "id": "51d1dabb" }, "source": [ "Bei jedem Schleifendurchlauf wird `pair` ein Tupel mit Ergebnissen zugewiesen.\n", "Jetzt können wir die Ergebnisse Variablen zuweisen und dann folgendermaßen die Siege des ersten Teams zählen:" ] }, { "cell_type": "code", "execution_count": null, "id": "7eb73d5d", "metadata": { "id": "7eb73d5d" }, "outputs": [], "source": [ "siege = 0\n", "for team1, team2 in zip(ergebnisse1, ergebnisse2):\n", " if team1 > team2:\n", " siege += 1\n", "\n", "siege" ] }, { "cell_type": "markdown", "id": "ad740fcd", "metadata": { "id": "ad740fcd" }, "source": [ "Leider hat die erste Mannschaft nur drei Spiele gewonnen und damit diese Serie verloren.\n", "\n", "Wenn wir zwei Listen haben und eine Liste mit Paaren haben wollen, können wir `zip` und `list` verwenden:" ] }, { "cell_type": "code", "execution_count": null, "id": "9529baa8", "metadata": { "id": "9529baa8" }, "outputs": [], "source": [ "t = list(zip(ergebnisse1, ergebnisse2))\n", "t" ] }, { "cell_type": "markdown", "id": "ec4249fa", "metadata": { "id": "ec4249fa" }, "source": [ "Das Ergebnis ist eine Liste mit Tupeln, also können wir so das Ergebnis des letzten Spiels bekommen:" ] }, { "cell_type": "code", "execution_count": null, "id": "dbde77b8", "metadata": { "id": "dbde77b8" }, "outputs": [], "source": [ "t[-1]" ] }, { "cell_type": "markdown", "id": "436486b9", "metadata": { "id": "436486b9" }, "source": [ "Wenn wir eine Liste mit Schlüsseln und eine Liste mit Werten haben, können wir `zip` und `dict` verwenden, um ein assoziatives Datenfeld zu erstellen.\n", "So können wir zum Beispiel ein assoziatives Datenfeld erstellen, das von jedem Buchstaben auf seine Position im Alphabet verweist:" ] }, { "cell_type": "code", "execution_count": null, "id": "dbb7d0b3", "metadata": { "id": "dbb7d0b3" }, "outputs": [], "source": [ "buchstaben = 'abcdefghijklmnopqrstuvwxyz'\n", "zahlen = range(len(buchstaben))\n", "buchstaben_verweis = dict(zip(buchstaben, zahlen))" ] }, { "cell_type": "markdown", "id": "b4de6974", "metadata": { "id": "b4de6974" }, "source": [ "Jetzt können wir nach einem Buchstaben suchen und erhalten den Index von dessen Position im Alphabet:" ] }, { "cell_type": "code", "execution_count": null, "id": "49e3fd8e", "metadata": { "id": "49e3fd8e" }, "outputs": [], "source": [ "buchstaben_verweis['a'], buchstaben_verweis['z']" ] }, { "cell_type": "markdown", "id": "cc632542", "metadata": { "id": "cc632542" }, "source": [ "In diesem Mapping ist der Index von `'a'` `0` und der Index von `'z'` `25`.\n", "\n", "Wenn wir in einer Schleife durch die Elemente einer Folge und deren Indizes gehen müssen, können wir die eingebaute Funktion `enumerate` verwenden:" ] }, { "cell_type": "code", "execution_count": null, "id": "9e4f3e51", "metadata": { "id": "9e4f3e51" }, "outputs": [], "source": [ "enumerate('abc')" ] }, { "cell_type": "markdown", "id": "92ad45bb", "metadata": { "id": "92ad45bb" }, "source": [ "Das Ergebnis ist ein **Enumerate-Objekt** (Deutsch etwa: *Aufzählungs-Objekt*), das eine Folge von Paaren in einer Schleife durchläuft, wobei jedes Paar einen Index (beginnend bei `0`) und ein Element aus der angegebenen Folge enthält:" ] }, { "cell_type": "code", "execution_count": null, "id": "c1dcb46d", "metadata": { "id": "c1dcb46d" }, "outputs": [], "source": [ "for index, element in enumerate('abc'):\n", " print(index, element)" ] }, { "cell_type": "markdown", "id": "cf0b55d7", "metadata": { "id": "cf0b55d7" }, "source": [ "## Vergleichen und Sortieren\n", "\n", "Die relationalen Operatoren funktionieren auch mit Tupeln und anderen Folgen.\n", "Wenn wir zum Beispiel den `<`-Operator mit Tupeln verwenden, beginnt er damit, das erste Element jeder Folge zu vergleichen.\n", "Wenn diese gleich sind, geht er zum nächsten Elemente-Paar weiter, und so weiter und so fort, bis er ein Paar findet, das sich unterscheidet:" ] }, { "cell_type": "code", "execution_count": null, "id": "aed20c28", "metadata": { "id": "aed20c28" }, "outputs": [], "source": [ "(0, 1, 2) < (0, 3, 4)" ] }, { "cell_type": "markdown", "id": "65ceea86", "metadata": { "id": "65ceea86" }, "source": [ "Nachfolgende Elemente werden nicht mehr betrachtet -- selbst wenn sie sehr groß sind:" ] }, { "cell_type": "code", "execution_count": null, "id": "4d9e73b3", "metadata": { "id": "4d9e73b3" }, "outputs": [], "source": [ "(0, 1, 2000000) < (0, 3, 4)" ] }, { "cell_type": "markdown", "id": "55e4a35e", "metadata": { "id": "55e4a35e" }, "source": [ "Diese Art Tupel zu vergleichen ist praktisch, um eine Liste von Tupeln zu sortieren, oder um ein Minimum oder Maximum zu finden.\n", "Lasst uns als ein Beispiel den häufigsten Buchstaben in einem Wort finden.\n", "Im vorherigen Kapitel haben wir `wert_zaehlen` geschrieben, was eine Zeichenkette aufnimmt und ein assoziatives Datenfeld zurückgibt, in dem jeder Buchstabe auf die Anzahl der Male, die er vorkommt verweist:" ] }, { "cell_type": "code", "execution_count": null, "id": "2077dfa9", "metadata": { "id": "2077dfa9" }, "outputs": [], "source": [ "def wert_zaehlen(string):\n", " zaehler = {}\n", " for buchstabe in string:\n", " if buchstabe not in zaehler:\n", " zaehler[buchstabe] = 1\n", " else:\n", " zaehler[buchstabe] += 1\n", " return zaehler" ] }, { "cell_type": "markdown", "id": "a80012c1", "metadata": { "id": "a80012c1" }, "source": [ "Hier ist das Ergebnis für die Zeichenkette `'banana'`:" ] }, { "cell_type": "code", "execution_count": null, "id": "b3d40516", "metadata": { "id": "b3d40516" }, "outputs": [], "source": [ "zaehler = wert_zaehlen('banana')\n", "zaehler" ] }, { "cell_type": "markdown", "id": "cc1ea4a7", "metadata": { "id": "cc1ea4a7" }, "source": [ "Mit nur drei Elementen können wir leicht sehen, dass der häufigste Buchstabe das `'a'` ist, das dreimal vorkommt.\n", "Aber gäbe es mehr Elemente, dann wäre es praktisch, diese automatisch sortieren zu können.\n", "\n", "Wir können so die Elemente von `zaehler` erhalten:" ] }, { "cell_type": "code", "execution_count": null, "id": "8288c28f", "metadata": { "id": "8288c28f" }, "outputs": [], "source": [ "elemente = zaehler.items()\n", "elemente" ] }, { "cell_type": "markdown", "id": "ac8dea7a", "metadata": { "id": "ac8dea7a" }, "source": [ "Das Ergebnis ist ein `dict_items`-Objekt, das sich wie eine Liste von Tupeln verhält, wir können es also folgendermaßen sortieren:" ] }, { "cell_type": "code", "execution_count": null, "id": "bbbade35", "metadata": { "id": "bbbade35" }, "outputs": [], "source": [ "sorted(elemente)" ] }, { "cell_type": "markdown", "id": "b690d17a", "metadata": { "id": "b690d17a" }, "source": [ "Standardmäßig wird das erste Element aus jedem Tupel zum Sortieren der Liste verwendet und im Fall eines Gleichstands auf das zweite Element zurückgegriffen.\n", "\n", "Um aber das Element mit dem höchsten Zähler zu finden, wollen wir das zweite Element zum Sortieren der Liste verwenden.\n", "Wir können das tun, indem wir eine Funktion schreiben, die ein Tupel nimmt und dessen zweites Element zurückgibt:" ] }, { "cell_type": "code", "execution_count": null, "id": "a4c31795", "metadata": { "id": "a4c31795" }, "outputs": [], "source": [ "def zweites_element(t):\n", " return t[1]" ] }, { "cell_type": "markdown", "id": "b964aa14", "metadata": { "id": "b964aa14" }, "source": [ "Dann können wir diese Funktion an `sorted` als optionales Element namens `key` weitergeben, was anzeigt, dass diese Funktion verwendet werden soll, um den **Sortier-Schlüssel** (Englisch: *sort key*) für jedes Element zu berechnen:" ] }, { "cell_type": "code", "execution_count": null, "id": "f3d3619a", "metadata": { "id": "f3d3619a" }, "outputs": [], "source": [ "elemente_sortiert = sorted(elemente, key=zweites_element)\n", "elemente_sortiert" ] }, { "cell_type": "markdown", "id": "4dc96848", "metadata": { "id": "4dc96848" }, "source": [ "Der Sortier-Schlüssel bestimmt die Reihenfolge der Elemente in der Liste.\n", "Der Buchstabe mit der niedrigsten Zahl erscheint zuerst, der Buchstabe mit der höchsten Zahl zuletzt.\n", "So können wir wie folgt den häufigsten Buchstaben finden:" ] }, { "cell_type": "code", "execution_count": null, "id": "f078c8a6", "metadata": { "id": "f078c8a6" }, "outputs": [], "source": [ "elemente_sortiert[-1]" ] }, { "cell_type": "markdown", "id": "d0d8b999", "metadata": { "id": "d0d8b999" }, "source": [ "Wenn wir nur das Maximum wollen, müssen wir die Liste nicht sortieren.\n", "Wir können `max` verwenden, was ebenfalls `key` als optionales Argument aufnimmt:" ] }, { "cell_type": "code", "execution_count": null, "id": "54030d8f", "metadata": { "id": "54030d8f" }, "outputs": [], "source": [ "max(elemente, key=zweites_element)" ] }, { "cell_type": "markdown", "id": "8a8327df", "metadata": { "id": "8a8327df" }, "source": [ "Um den Buchstaben mit der niedrigsten Zahl zu finden, könnten wir `min` auf die gleiche Weise nutzen." ] }, { "cell_type": "markdown", "id": "a62394a5", "metadata": { "id": "a62394a5" }, "source": [ "## Assoziatives Datenfeld umkehren\n", "\n", "Nehmen wir an, wir wollen ein assoziatives Datenfeld invertieren, sodass wir nach einem Wert suchen können und dessen korrespondierenden Schlüssel erhalten.\n", "Wenn wir zum Beispiel einen Wortzähler haben, der von jedem Wort auf die Anzahl von dessen Vorkommen verweist, können wir ein assoziatives Datenfeld erstellen, das von ganzen Zahlen auf die Wörter verweist, die entsprechend oft vorkommen.\n", "\n", "Aber es gibt ein Problem -- die Schlüssel in einem assoziativen Datenfeld müssen im Gegensatz zu den Werten einmalig sein. In einem Wortzähler könnte es zum Beispiel viele Wörter geben, die gleich oft vorkommen.\n", "\n", "Ein Weg, das assoziative Datenfeld umzukehren ist also, ein neues assoziatives Datenfeld zu erstellen, in dem die Werte Listen mit Schlüsseln aus dem ursprünglichen Datenfeld sind.\n", "Lasst uns zum Beispiel die Buchstaben in `papagei` zählen." ] }, { "cell_type": "code", "execution_count": null, "id": "ef158f81", "metadata": { "id": "ef158f81" }, "outputs": [], "source": [ "d = wert_zaehlen('papagei')\n", "d" ] }, { "cell_type": "markdown", "id": "f4570eae", "metadata": { "id": "f4570eae" }, "source": [ "Wenn wir dieses assoziative Datenfeld umkehren, sollte das Ergebnis `{2: ['p', 'a'], 1: ['g', 'e', 'i']}` sein, was darauf hinweist, dass die Buchstaben, die einmal vorkommen `'g'`, `'e'` und `'i'`, und die Buchstaben, die zweimal vorkommen `'p'` und `'a'` sind.\n", "\n", "Die folgende Funktion nimmt ein assoziatives Datenfeld und gibt dieses umgekehrt als neues assoziatives Datenfeld zurück:" ] }, { "cell_type": "code", "execution_count": null, "id": "d3607b8d", "metadata": { "id": "d3607b8d" }, "outputs": [], "source": [ "def invertiert_dict(d):\n", " neu = {}\n", " for schluessel, wert in d.items():\n", " if wert not in neu:\n", " neu[wert] = [schluessel]\n", " else:\n", " neu[wert].append(schluessel)\n", " return neu" ] }, { "cell_type": "markdown", "id": "ca5fa025", "metadata": { "id": "ca5fa025" }, "source": [ "Die `for`-Anweisung geht in einer Schleife durch die Schlüssel und Werte in `d`.\n", "Wenn sich ein Wert noch nicht in dem neuen assoziativen Datenfeld befindet, wird er hinzugefügt und mit einer Liste verknüpft, die ein einzelnes Element enthält.\n", "Andernfalls wird er an die bestehende Liste angehängt.\n", "\n", "Wir können das so testen:" ] }, { "cell_type": "code", "execution_count": null, "id": "692d9cf8", "metadata": { "id": "692d9cf8" }, "outputs": [], "source": [ "invertiert_dict(d)" ] }, { "cell_type": "markdown", "id": "4cfb1693", "metadata": { "id": "4cfb1693" }, "source": [ "Und wir erhalten das erwartete Ergebnis,\n", "\n", "Das ist das erste Beispiel, das wir gesehen haben, in dem die Werte eines assoziativen Datenfelds Listen sind.\n", "Weitere werden folgen!" ] }, { "cell_type": "markdown", "id": "6d138cd7", "metadata": { "id": "6d138cd7", "tags": [] }, "source": [ "## Debuggen\n", "\n", "Listen, assoziative Datenfelder und Tupel sind **Datenstrukturen** (Englisch: *data structures*).\n", "In diesem Kapitel beginnen wir, zusammengesetzte Datenstrukturen zu sehen, wie Listen von Tupeln oder assoziative Datenfelder, die Tupel als Schlüssel und Listen als Werte enthalten.\n", "Zusammengesetzte Datenstrukturen sind praktisch, aber auch anfällig für Fehler, die zum Beispiel vorkommen wenn eine Datenstruktur den falschen Datentyp, die falsche Größe oder Struktur hat.\n", "Wenn eine Funktion zum Beispiel eine Liste aus ganzen Zahlen erwartet und Sie ihr nur eine einfache ganze Zahl geben (nicht in einer Liste), wird sie wahrscheinlich nicht funktionieren.\n", "\n", "Um beim Debuggen dieser Art von Fehler zu helfen, habe ich ein Modul namens `structshape` geschrieben, das eine Funktion, die ebenfalls `structshape` heißt, zur Verfügung stellt. Diese nimmt jegliche Art von Datenstruktur als Argument auf und gibt eine Zeichenkette zurück, die deren Struktur beschreibt.\n", "Sie können es hier herunterladen:\n", "<https://raw.githubusercontent.com/AllenDowney/ThinkPython/v3/structshape.py>." ] }, { "cell_type": "code", "execution_count": null, "id": "e9f03e91", "metadata": { "id": "e9f03e91", "tags": [] }, "outputs": [], "source": [ "download('https://raw.githubusercontent.com/AllenDowney/ThinkPython/v3/structshape.py');" ] }, { "cell_type": "markdown", "id": "646f4d55", "metadata": { "id": "646f4d55" }, "source": [ "Wir können es so importieren:" ] }, { "cell_type": "code", "execution_count": null, "id": "90ab624a", "metadata": { "id": "90ab624a" }, "outputs": [], "source": [ "from structshape import structshape" ] }, { "cell_type": "markdown", "id": "86cc6ccc", "metadata": { "id": "86cc6ccc" }, "source": [ "Hier ist ein Beispiel mit einer einfachen Liste:" ] }, { "cell_type": "code", "execution_count": null, "id": "6794330f", "metadata": { "id": "6794330f" }, "outputs": [], "source": [ "t = [1, 2, 3]\n", "structshape(t)" ] }, { "cell_type": "markdown", "id": "9de4f6ec", "metadata": { "id": "9de4f6ec" }, "source": [ "Hier ist eine Liste mit Listen:" ] }, { "cell_type": "code", "execution_count": null, "id": "54cd185b", "metadata": { "id": "54cd185b" }, "outputs": [], "source": [ "t2 = [[1,2], [3,4], [5,6]]\n", "structshape(t2)" ] }, { "cell_type": "markdown", "id": "aced9984", "metadata": { "id": "aced9984" }, "source": [ "Wenn die Elemente einer Liste nicht dem gleichen Datentypen entsprechen, gruppiert sie `structshape` nach Typ:" ] }, { "cell_type": "code", "execution_count": null, "id": "04028afd", "metadata": { "id": "04028afd" }, "outputs": [], "source": [ "t3 = [1, 2, 3, 4.0, '5', '6', [7], [8], 9]\n", "structshape(t3)" ] }, { "cell_type": "markdown", "id": "f63ff690", "metadata": { "id": "f63ff690" }, "source": [ "Hier ist eine Liste aus Tupeln:" ] }, { "cell_type": "code", "execution_count": null, "id": "b5d45c88", "metadata": { "id": "b5d45c88" }, "outputs": [], "source": [ "s = 'abc'\n", "lt = list(zip(t, s))\n", "structshape(lt)" ] }, { "cell_type": "markdown", "id": "c9ec67eb", "metadata": { "id": "c9ec67eb" }, "source": [ "Und hier ist ein assoziatives Datenfeld mit drei Elementen, das von ganzen Zahlen zu Zeichenketten verweist:" ] }, { "cell_type": "code", "execution_count": null, "id": "15131907", "metadata": { "id": "15131907" }, "outputs": [], "source": [ "d = dict(lt)\n", "structshape(d)" ] }, { "cell_type": "markdown", "id": "f29bb82b", "metadata": { "id": "f29bb82b" }, "source": [ "Wenn es Ihnen Schwierigkeiten bereitet, einen Überblick über Ihre Datenstrukturen zu behalten, kann `structshape` helfen." ] }, { "cell_type": "markdown", "id": "7a113d88-a078-4b89-b887-ccaed7981eef", "metadata": { "jp-MarkdownHeadingCollapsed": true }, "source": [ "## Glossar\n", "\n", "Legen wir uns eine Liste mit den wichtigsten Begriffen an, die wir im Kapitel 11 gelernt haben:\n", "\n", "- verpacken:\n", "- entpacken:\n", "- Zip-Objekt:\n", "- Enumerate-Objekt:\n", "- Sortier-Schlüssel:\n", "- Datenstruktur:\n", "\n", "Ergänzen Sie die Liste in eigenen Worten. Das ist eine gute Erinnerungs- und Übungsmöglichkeit.\n" ] }, { "cell_type": "markdown", "id": "1471b3c0", "metadata": { "id": "1471b3c0" }, "source": [ "## Übung" ] }, { "cell_type": "code", "execution_count": null, "id": "c65d68d2", "metadata": { "id": "c65d68d2", "tags": [] }, "outputs": [], "source": [ "# Diese Zelle weist Jupyter an, detallierte Debugging-Informationen auszugeben, wenn ein Laufzeitfehler\n", "# passiert. Lassen Sie sie daher laufen, bevor Sie beginnen an den Aufgaben zu arbeiten.\n", "\n", "%xmode Verbose" ] }, { "cell_type": "markdown", "id": "97a0352d", "metadata": { "id": "97a0352d" }, "source": [ "### Fragen Sie einen virtuellen Assistenten\n", "\n", "Die Übungen in diesem Kapitel könnten etwas schwieriger sein, als die in vorherigen Kapiteln, daher möchte ich Sie ermutigen, einen virtuellen Assistenten um Hilfe zu fragen.\n", "Wenn Sie schwierigere Fragen stellen könnte Ihnen auffallen, dass die Antworten beim ersten Versuch noch nicht richtig sind, daher ist dies eine gute Möglichkeit, das Schreiben von guten Prompts und nachfolgenden Verfeinerungen zu üben.\n", "\n", "Eine Strategie, die Sie in Erwägung ziehen könnten, ist das Zerteilen von großen Problemen in kleinere Teile, die mit einfachen Funktionen gelöst werden können.\n", "Bitten Sie einen virtuellen Assistenten darum, diese Funktionen zu schreiben und zu testen.\n", "Dann, sobald diese funktionieren, fragen Sie nach einer Lösung für das urspüngliche Problem.\n", "\n", "Bei einige der Aufgaben unten mache ich Vorschläge, welche Datenstrukturen oder Algorithmen sich hier eignen würden.\n", "Sie finden die Vorschläge vielleicht hilfreich, während Sie an den Aufgaben arbeiten, diese sind aber auch gute Prompts zum Weitergeben an einen virtuellen Assistenten." ] }, { "cell_type": "markdown", "id": "f90e011f", "metadata": { "id": "f90e011f" }, "source": [ "### Aufgabe 1\n", "\n", "In diesem Kapitel habe ich gesagt, dass Tupel als Schlüssel in assoziativen Datenfeldern genutzt werden können, weil sie unveränderlich und dadurch hashbar sind.\n", "Das ist aber nicht immer der Fall.\n", "\n", "Wenn ein Tupel einen veränderbaren Wert wie etwa eine Liste oder ein assoziatives Datenfeld enthält, ist es nicht mehr hashbar, da es Elemente enthält, die nicht hashbar sind. Hier ist zum Beispiel ein Tupel, das zwei Listen mit ganzen Zahlen enthält:" ] }, { "cell_type": "code", "execution_count": null, "id": "4416fe4a", "metadata": { "id": "4416fe4a" }, "outputs": [], "source": [ "liste0 = [1, 2, 3]\n", "liste1 = [4, 5]\n", "\n", "t = (liste0, liste1)\n", "t" ] }, { "cell_type": "markdown", "id": "02799077", "metadata": { "id": "02799077" }, "source": [ "Schreiben Sie eine Code-Zeile, die den Wert `6` an das Ende der zweiten Liste in `t` anhängt. Wenn Sie `t` ausgeben sollte das Ergebnis `([1, 2, 3], [4, 5, 6])` sein." ] }, { "cell_type": "code", "execution_count": null, "id": "e6eda0e4", "metadata": { "id": "e6eda0e4", "tags": [] }, "outputs": [], "source": [ "# Lösung hierhin schreiben" ] }, { "cell_type": "markdown", "id": "644b1dfb", "metadata": { "id": "644b1dfb" }, "source": [ "Versuchen Sie, ein assoziatives Datenfeld zu erstellen, das von `t` auf eine Zeichenkette verweist und überprüfen Sie, ob Sie einen `TypeError` erhalten." ] }, { "cell_type": "code", "execution_count": null, "id": "4fae1acc", "metadata": { "id": "4fae1acc", "tags": [] }, "outputs": [], "source": [ "# Lösung hierhin schreiben" ] }, { "cell_type": "markdown", "id": "fb77a352", "metadata": { "id": "fb77a352" }, "source": [ "Für mehr Informationen zu diesem Thema fragen Sie einen virtuellen Assistenten: \"Sind Tupel in Python immer hashbar?\"." ] }, { "cell_type": "markdown", "id": "bdfc8c27", "metadata": { "id": "bdfc8c27", "tags": [] }, "source": [ "### Aufgabe 2\n", "\n", "In diesem Kapitel haben wir ein assoziatives Datenfeld erstellt, dass von jedem Buchstaben zu dessen Index im Alphabet verweist:" ] }, { "cell_type": "code", "execution_count": null, "id": "855c7ed2", "metadata": { "id": "855c7ed2" }, "outputs": [], "source": [ "buchstaben = 'abcdefghijklmnopqrstuvwxyz'\n", "zahlen = range(len(buchstaben))\n", "buchstaben_verweis = dict(zip(buchstaben, zahlen))" ] }, { "cell_type": "markdown", "id": "a8cd720b", "metadata": { "id": "a8cd720b" }, "source": [ "Der Index von `'a'` ist zum Beispiel `0`:" ] }, { "cell_type": "code", "execution_count": null, "id": "3c921f68", "metadata": { "id": "3c921f68" }, "outputs": [], "source": [ "buchstaben_verweis['a']" ] }, { "cell_type": "markdown", "id": "a04c25db", "metadata": { "id": "a04c25db" }, "source": [ "Um in die andere Richtung zu gehen, können wir Listen-Indexierung verwenden.\n", "Der Buchstabe am Index `1` ist zum Beispiel `'b'`:" ] }, { "cell_type": "code", "execution_count": null, "id": "b029b0da", "metadata": { "id": "b029b0da" }, "outputs": [], "source": [ "buchstaben[1]" ] }, { "cell_type": "markdown", "id": "165ab770", "metadata": { "id": "165ab770" }, "source": [ "Wir können `buchstaben_verweis` und `buchstaben` verwenden, um Wörter mittels einer Cäsar-Chiffre zu ver- und entschlüsseln.\n", "\n", "Eine Cäsar-Chiffre ist eine schwache Form der Verschlüsselung, bei der jeder Buchstabe um eine festgelegte Zahl von Stellen im Alphabet verschoben wird. Würde eine Verschiebung über `z` hinausgehen, wird wieder bei `a` begonnen. Zum Beispiel ist `'a'` um `2` verschoben `'c'` und `'z'` um `1` verschoben ist `'a'`.\n", "\n", "Schreiben sie eine Funktion namens `wort_verschieben`, die als Parameter eine Zeichenkette und eine ganze Zahl aufnimmt und eine neue Zeichenkette zurückgibt, die alle Buchstaben der ursprünglichen Zeichenkette verschoben um die angegebene Anzahl Stellen enthält.\n", "\n", "Um Ihre Funktion zu testen, überprüfen Sie, ob `\"cheer\"` um `7` Stellen verschoben `\"jolly\"` und `\"melon\"`um `16` Stellen verschoben `\"cubed\"` ergibt.\n", "\n", "Tipps: Verwenden Sie den Modulus-Operator um vom `z` wieder zu `a` zurückzuspringen.\n", "Gehen Sie in einer Schleife die Buchstaben des Worts, verschieben Sie diese und hängen das Ergebnis an das Ende einer Liste mit Buchstaben an.\n", "Verwenden Sie dann `join`, um die Buchstaben zu einer Zeichenkette zu konkatenieren." ] }, { "cell_type": "markdown", "id": "e7478b18", "metadata": { "id": "e7478b18", "tags": [] }, "source": [ "Verwenden Sie als Starthilfe diese Gliederung:" ] }, { "cell_type": "code", "execution_count": null, "id": "1cc07036", "metadata": { "id": "1cc07036", "tags": [] }, "outputs": [], "source": [ "def wort_verschieben(wort, n):\n", " \"\"\"Verschieben die Buchstaben von `wort` um `n` Stellen.\n", "\n", " >>> wort_verschieben('cheer', 7)\n", " 'jolly'\n", " >>> wort_verschieben('melon', 16)\n", " 'cubed'\n", " \"\"\"\n", " return None" ] }, { "cell_type": "code", "execution_count": null, "id": "96560a0e", "metadata": { "id": "96560a0e" }, "outputs": [], "source": [ "# Lösung hierhin schreiben" ] }, { "cell_type": "code", "execution_count": null, "id": "c026c6d1", "metadata": { "id": "c026c6d1", "tags": [] }, "outputs": [], "source": [ "wort_verschieben('cheer', 7)" ] }, { "cell_type": "code", "execution_count": null, "id": "5814999d", "metadata": { "id": "5814999d", "tags": [] }, "outputs": [], "source": [ "wort_verschieben('melon', 16)" ] }, { "cell_type": "markdown", "id": "39a67af9", "metadata": { "id": "39a67af9", "tags": [] }, "source": [ "Sie können `doctest` verwenden, um Ihre Funktion zu testen:" ] }, { "cell_type": "code", "execution_count": null, "id": "9464d140", "metadata": { "id": "9464d140", "tags": [] }, "outputs": [], "source": [ "from doctest import run_docstring_examples\n", "\n", "def run_doctests(func):\n", " run_docstring_examples(func, globals(), name=func.__name__)\n", "\n", "run_doctests(wort_verschieben)" ] }, { "cell_type": "markdown", "id": "779f13af", "metadata": { "id": "779f13af" }, "source": [ "### Aufgabe 3\n", "\n", "Schreiben Sie eine Funktion namens `haeufigste_buchstaben`, die eine Zeichenkette aufnimmt und die einzelnen Buchstaben in absteigender Reihenfolge ihrer Häufigkeit ausgibt.\n", "\n", "Um die Elemente in absteigender Reihenfolge zu erhalten können Sie `reversed` zusammen mit `sorted` verwenden, oder Sie können `reverse=True` als Schlüsselwort-Parameter an `sorted` übergeben." ] }, { "cell_type": "markdown", "id": "d71923e6", "metadata": { "id": "d71923e6", "tags": [] }, "source": [ "Als Einstieg können Sie diese Skizze der Funktion verwenden:" ] }, { "cell_type": "code", "execution_count": null, "id": "4309d0b5", "metadata": { "id": "4309d0b5", "tags": [] }, "outputs": [], "source": [ "def haeufigste_buchstaben(zeichenkette):\n", " return None" ] }, { "cell_type": "code", "execution_count": null, "id": "52228828", "metadata": { "id": "52228828" }, "outputs": [], "source": [ "# Lösung hierhin schreiben" ] }, { "cell_type": "markdown", "id": "c6354c44", "metadata": { "id": "c6354c44", "tags": [] }, "source": [ "Und dieses Beispiel können Sie verwenden, um Ihre Funktion zu testen:" ] }, { "cell_type": "code", "execution_count": null, "id": "3bf2aa0d", "metadata": { "id": "3bf2aa0d", "tags": [] }, "outputs": [], "source": [ "haeufigste_buchstaben('brontosaurus')" ] }, { "cell_type": "markdown", "id": "2ca1e337", "metadata": { "id": "2ca1e337", "tags": [] }, "source": [ "Sobald Ihre Funktion funktioniert können Sie den folgenden Code verwenden, um die häufigsten Buchstaben aus *Dracula* auszugeben (der Text kann über Projekt Gutenberg heruntergeladen werden):" ] }, { "cell_type": "code", "execution_count": null, "id": "e4fbf5d9", "metadata": { "id": "e4fbf5d9", "tags": [] }, "outputs": [], "source": [ "download('https://www.gutenberg.org/cache/epub/345/pg345.txt');" ] }, { "cell_type": "code", "execution_count": null, "id": "817ec689", "metadata": { "id": "817ec689", "tags": [] }, "outputs": [], "source": [ "zeichenkette = open('pg345.txt').read()\n", "haeufigste_buchstaben(zeichenkette)" ] }, { "cell_type": "markdown", "id": "211c09c9", "metadata": { "id": "211c09c9", "tags": [] }, "source": [ "Nach Zims \"Codes and Secret Writing\" beginnt die Buchstabenfolge für die englische Sprache, absteigend nach Häufigkeit sortiert, mit \"ETAONRISH\".\n", "Wie lässt sich diese Folge mit den Ergebnissen von *Dracula* vergleichen?" ] }, { "cell_type": "markdown", "id": "cbe9933e", "metadata": { "id": "cbe9933e" }, "source": [ "### Aufgabe 4\n", "\n", "In einer vorherigen Übung haben wir getestet, ob zwei Zeichenketten Anagramme voneinander sind, indem wir die Buchstaben beider Wörter sortiert, und überprüft haben ob diese gleich sind.\n", "Lasst uns nun das gleiche Problem etwas herausfordernder machen.\n", "\n", "Wir werden ein Programm schreiben, das eine Wortliste aufnimmt und alle Wortgruppen daraus ausgibt, die Anagramme sind.\n", "Der Output könnte zum Beispiel so aussehen:\n", "\n", " ['deltas', 'desalt', 'lasted', 'salted', 'slated', 'staled']\n", " ['retainers', 'ternaries']\n", " ['generating', 'greatening']\n", " ['resmelts', 'smelters', 'termless']\n", "\n", "Tipp: Sortieren Sie für jedes Wort in der Liste die Buchstaben und verbinden Sie zu einer Zeichenkette. Erstellen Sie nun ein assoziatives Datenfeld `anagramm_dict`, das von dieser geordneten Zeichenkette auf eine Liste mit Wörtern verweist, die dessen Anagramme sind." ] }, { "cell_type": "markdown", "id": "4b9ed2a8", "metadata": { "id": "4b9ed2a8", "tags": [] }, "source": [ "Die folgende Zelle lädt `words.txt` herunter und liest die Wörter in eine Liste ein:" ] }, { "cell_type": "code", "execution_count": null, "id": "941719c1", "metadata": { "id": "941719c1", "tags": [] }, "outputs": [], "source": [ "download('https://raw.githubusercontent.com/AllenDowney/ThinkPython/v3/words.txt');" ] }, { "cell_type": "code", "execution_count": null, "id": "d2ec641b", "metadata": { "id": "d2ec641b", "tags": [] }, "outputs": [], "source": [ "wort_liste = open('words.txt').read().split()" ] }, { "cell_type": "markdown", "id": "e4cc2c8c", "metadata": { "id": "e4cc2c8c", "tags": [] }, "source": [ "Hier ist die `wort_sortieren`-Funktion, die wir schon verwendet haben:" ] }, { "cell_type": "code", "execution_count": null, "id": "7ae29f73", "metadata": { "id": "7ae29f73", "tags": [] }, "outputs": [], "source": [ "def wort_sortieren(wort):\n", " return ''.join(sorted(wort))" ] }, { "cell_type": "code", "execution_count": null, "id": "013819a5", "metadata": { "id": "013819a5" }, "outputs": [], "source": [ "# Lösung hierhin schreiben" ] }, { "cell_type": "markdown", "id": "70faa9f5", "metadata": { "id": "70faa9f5", "tags": [] }, "source": [ "Um die längste Liste mit Anagrammen zu finden können Sie die folgende Funktion verwenden, die ein Schlüssel-Wert-Paar aufnimmt, bei dem der Schlüssel eine Zeichenkette und der Wert eine Liste mit Wörtern ist.\n", "Sie gibt die Länge der Liste zurück:" ] }, { "cell_type": "code", "execution_count": null, "id": "fbf9ede3", "metadata": { "id": "fbf9ede3", "tags": [] }, "outputs": [], "source": [ "def wert_laenge(paar):\n", " schluessel, wert = paar\n", " return len(wert)" ] }, { "cell_type": "markdown", "id": "dcda6e28", "metadata": { "id": "dcda6e28", "tags": [] }, "source": [ "Wir können diese Funktion als Sortierschlüssel verwenden, um die längste Anagramm-Liste zu finden:" ] }, { "cell_type": "code", "execution_count": null, "id": "55435050", "metadata": { "id": "55435050", "tags": [] }, "outputs": [], "source": [ "anagramm_elemente = sorted(anagramm_dict.items(), key=wert_laenge)\n", "for schluessel, wert in anagramm_elemente[-10:]:\n", " print(wert)" ] }, { "cell_type": "markdown", "id": "0b6d5add", "metadata": { "id": "0b6d5add", "tags": [] }, "source": [ "Wenn Sie die längsten Wörter, von denen es Anagramme gibt erfahren wollen, verwenden Sie die folgende Schleife um einige von ihnen auszugeben:" ] }, { "cell_type": "code", "execution_count": null, "id": "6a9320c2", "metadata": { "id": "6a9320c2", "tags": [] }, "outputs": [], "source": [ "laengste = 7\n", "\n", "for schluessel, wert in anagramm_elemente:\n", " if len(wert) > 1:\n", " wort_laenge = len(wert[0])\n", " if wort_laenge > laengste:\n", " laengste = wort_laenge\n", " print(wert)" ] }, { "cell_type": "markdown", "id": "4fbe939e", "metadata": { "id": "4fbe939e" }, "source": [ "### Aufgabe 5\n", "\n", "Schreiben Sie eine Funktion namens `wort_distanz`, die zwei Wörter derselben Länge aufnimmt, und die Anzahl der Stellen zurückgibt, an denen sich diese unterscheiden.\n", "\n", "Tipp: Verwenden Sie `zip`, um in einer Schleife durch die korrespondierenden Buchstaben der Wörter zu gehen." ] }, { "cell_type": "markdown", "id": "8b48dbdc", "metadata": { "id": "8b48dbdc", "tags": [] }, "source": [ "Hier ist eine Gliederung der Funktion mit Doctests, die Sie verwenden können, um Ihre Funktion zu testen:" ] }, { "cell_type": "code", "execution_count": null, "id": "3d5a75f8", "metadata": { "id": "3d5a75f8", "tags": [] }, "outputs": [], "source": [ "def wort_distanz(wort1, wort2):\n", " \"\"\"Berechnet die Anzahl der Stellen, an denen sich die beiden Wörter unterscheiden.\n", "\n", " >>> wort_distanz(\"hello\", \"hxllo\")\n", " 1\n", " >>> wort_distanz(\"ample\", \"apply\")\n", " 2\n", " >>> wort_distanz(\"kitten\", \"mutton\")\n", " 3\n", " \"\"\"\n", " return None" ] }, { "cell_type": "code", "execution_count": null, "id": "a9816dde", "metadata": { "id": "a9816dde" }, "outputs": [], "source": [ "# Lösung hierhin schreiben" ] }, { "cell_type": "code", "execution_count": null, "id": "753a23c1", "metadata": { "id": "753a23c1", "tags": [] }, "outputs": [], "source": [ "from doctest import run_docstring_examples\n", "\n", "def run_doctests(func):\n", " run_docstring_examples(func, globals(), name=func.__name__)\n", "\n", "run_doctests(wort_distanz)" ] }, { "cell_type": "markdown", "id": "066eec59", "metadata": { "id": "066eec59" }, "source": [ "### Aufgabe 6\n", "\n", "\"Metathese“ bezeichnet das Vertauschen von Buchstaben in einem Wort.\n", "Zwei Wörter bilden ein \"Metathese-Paar“, wenn man das eine in das andere umwandeln kann, indem man zwei Buchstaben vertauscht, wie zum Beispiel bei `converse` und `conserve`.\n", "Schreiben Sie ein Programm, das alle Metathesepaare in der Wortliste findet.\n", "\n", "Tipp: Die Wörter in einem Metathesepaar müssen Anagramme voneinander sein.\n", "\n", "Credit: Diese Aufgabe ist inspiriert von einem Beispiel auf <http://puzzlers.org>." ] }, { "cell_type": "code", "execution_count": null, "id": "57649075", "metadata": { "id": "57649075" }, "outputs": [], "source": [ "# Lösung hierhin schreiben" ] }, { "cell_type": "markdown", "id": "6a028806", "metadata": { "id": "6a028806", "tags": [] }, "source": [ "### Aufgabe 7\n", "\n", "Dies ist eine Bonus-Aufgabe, die sich nicht im Buch befindet.\n", "Sie ist schwieriger als die anderen Übungen in diesem Kapitel, also fragen Sie vielleicht einen virtuellen Assistenten um Hilfe oder kommen Sie auf die Aufgabe zurück, nachdem Sie weitere Kapitel gelesen haben.\n", "\n", "Hier ist ein weiterer Car Talk Puzzler\n", "(<http://www.cartalk.com/content/puzzlers>):\n", "\n", "> Welches ist das längste englische Wort, das ein gültiges Wort bleibt,\n", "> nachdem man einen Buchstaben nach dem anderen entfernt hat?\n", ">\n", "> Buchstaben können von beiden Enden des Wortes und aus dessen Mitte entfernt werden, aber Sie dürfen\n", "> keine der Buchstaben vertauschen. Jedes Mal wenn Sie einen Buchstaben entfernen,\n", "> erhalten Sie ein neues Wort. Wenn Sie das eine Weile tun, enden Sie irgendwann bei einem\n", "> einzelnen Buchstaben und auch dieser wird ein englisches Wort sein --\n", "> eines, das im Wörterbuch steht. Ich will nun wissen, welches das längste Wort ist\n", "> und wie viele Buchstaben dieses hat.\n", ">\n", "> Ich werde hierfür ein kleines Beispiel geben: \"Sprite\". Ok? Wir beginnen mit\n", "> `\"sprite\"`, entfernen einen Buchstaben aus der Mitte des Wortes, in diesem Fall das `r`,\n", "> und erhalten das Wort `\"spite\"`, davon entfernen wir das `e` und bekommen `\"spit\"`,\n", "> wovon wir wiederum das `s` entfernen und `\"pit\"` bekommen, dann `\"it\"` und zuletzt `\"I\"`.\n", "\n", "Schreiben Sie ein Programm, das alle Wörter findet, die auf diese Weise reduziert werden können und finden Sie von diesen das schließlich das Längste.\n", "\n", "Diese Übung ist ein bisschen schwieriger als die meisten anderen, also sind hier einige Empfehlungen:\n", "\n", "1. Sie sollten vielleicht eine Funktion schreiben, die für ein Wort eine Liste aller Wörter berechnet, die daraus durch Entfernen eines Buchstabens gebildet werden können. Das sind die \"Kinder“ dieses Wortes.\n", "\n", "2. Rekursiv ist ein Wort reduzierbar, wenn eines seiner Kinder reduzierbar ist. Als Basisfall können Sie die leere Zeichenkette als reduzierbar betrachten.\n", "\n", "3. Die Wortliste, die wir bisher verwendet haben enthält keine Wörter aus einzelnen Buchstaben. Also sollten Sie vielleicht \"I\" und \"a\" hinzufügen.\n", "\n", "4. Um die Leistung Ihres Programmes zu verbessern könnten Sie die Wörter, von denen schon bekannt ist, dass sie reduzierbar sind memoisieren." ] }, { "cell_type": "code", "execution_count": null, "id": "c19bf833", "metadata": { "id": "c19bf833" }, "outputs": [], "source": [ "# Lösung hierhin schreiben" ] }, { "cell_type": "code", "execution_count": null, "id": "2d9764d6", "metadata": { "id": "2d9764d6" }, "outputs": [], "source": [ "# Lösung hierhin schreiben" ] }, { "cell_type": "code", "execution_count": null, "id": "5e4f5d8e", "metadata": { "id": "5e4f5d8e" }, "outputs": [], "source": [ "# Lösung hierhin schreiben" ] }, { "cell_type": "code", "execution_count": null, "id": "27d311dd", "metadata": { "id": "27d311dd" }, "outputs": [], "source": [ "# Lösung hierhin schreiben" ] }, { "cell_type": "code", "execution_count": null, "id": "68c27c7e", "metadata": { "id": "68c27c7e" }, "outputs": [], "source": [ "# Lösung hierhin schreiben" ] }, { "cell_type": "markdown", "id": "e01fcbe0-9b3f-4e8f-8e8a-807c50a2154a", "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", "id": "ca074907-29ea-4a54-9573-9e468d491860", "metadata": {}, "source": [ "\n", "\n", "Herzlichen Glückwunsch! Sie haben das 11. Kapitel geschafft." ] }, { "cell_type": "markdown", "id": "5cf853f4-5575-408c-8446-bc993452dc66", "metadata": {}, "source": [ "<img src=\"https://scm.cms.hu-berlin.de/ibi/python/-/raw/master/img/by-nc-sa.png\" alt=\"CC BY-NC-SA\" style=\"width: 150px;\"/>\n", "\n", "Der Text dieses Notebooks ist als freies Werk unter der Lizenz [CC BY-NC-SA 4.0 ](https://creativecommons.org/licenses/by-nc-sa/4.0/) verfügbar.\n", "Der Code dieses Notebooks ist als freies Werk unter der Lizenz [MIT License](https://mit-license.org/) verfügbar.\n", "\n", "Es handelt sich um übersetzte und leicht veränderte Notebooks von [Allen B. Downey](https://allendowney.com) aus [Think Python: 3rd Edition](https://allendowney.github.io/ThinkPython/index.html).\n" ] } ], "metadata": { "celltoolbar": "Tags", "colab": { "provenance": [] }, "kernelspec": { "display_name": "Python 3 (ipykernel)", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.12.2" } }, "nbformat": 4, "nbformat_minor": 5 }