"Eine Zuordnungstabelle enthält eine Sammlung von Indizes, die als Schlüssel bezeichnet werden, und eine Sammlung von Werten. Jeder Schlüssel ist mit einem einzigen Wert verbunden. Die Zuordnung eines Schlüssels und eines Wertes wird als Schlüssel-Wert-Paar oder manchmal als Element bezeichnet.\n",
"\n",
"Die Funktion `Dict` erstellt ein neues Wörterbuch ohne Elemente. Da `Dict` der Name einer integrierten Funktion ist, sollte man vermeiden ihn als Variablenname zu verwenden."
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "59ae8d17-a6f6-4210-95bc-0e7492bc8a7a",
"metadata": {},
"outputs": [],
"source": [
"eng2de = Dict()"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "28490ea3-c4d3-4e4d-915f-612ad36c9472",
"metadata": {},
"outputs": [],
"source": [
"eng2de[\"one\"] = \"ein\";"
]
},
{
"cell_type": "markdown",
"id": "09b62b4d-748b-4e1d-91f2-7db610ad02ac",
"metadata": {},
"source": [
"Du kannst auch eine Zuordnungstabelle mit Elementen wie folgt initialisieren:"
"Wörterbücher sind veränderbar, was bedeutet, dass es immer möglich ist, ihre Elemente zu ändern, neue Schlüssel-Wert-Paare hinzuzufügen oder bestehende zu entfernen."
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "22b295dc-994e-4bbc-ac8d-da5ab9e73493",
"metadata": {},
"outputs": [],
"source": [
"eng2de[\"four\"] = \"VIER\"\n",
"@show eng2de\n",
"eng2de[\"four\"] = \"vier\"\n",
"@show eng2de\n",
"delete!(eng2de, \"four\")\n",
"@show eng2de;"
]
},
{
"cell_type": "markdown",
"id": "d8b4ee03-5592-4567-883b-e0842fefeff4",
"metadata": {},
"source": [
"Im Allgemeinen ist die Reihenfolge der Schlüssel-Wert-Paare unvorhersehbar. Dies ist jedoch nicht wichtig, da die Werte immer mithilfe der Schlüssel abgerufen werden."
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "e9e4c5b9-9539-4294-a6ec-0a29fb76bf7b",
"metadata": {},
"outputs": [],
"source": [
"eng2de[\"two\"]"
]
},
{
"cell_type": "markdown",
"id": "84cf8b52-f061-4b28-a327-bc527820517c",
"metadata": {},
"source": [
"Wenn der Schlüssel nicht im Wörterbuch vorhanden ist, tritt eine Fehlermeldung auf:"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "8221269b-7a8f-4cfd-ab8e-0aa4fd6c962d",
"metadata": {},
"outputs": [],
"source": [
"eng2de[\"four\"]"
]
},
{
"cell_type": "markdown",
"id": "f83d51c5-2c1d-4449-8cd5-e80f224b9a76",
"metadata": {},
"source": [
"Die Funktion `length` funktioniert auch bei Zuordnungstabellen; sie gibt die Anzahl der Schlüssel-Wert-Paare zurück:"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "0e242667-b921-4242-bdcc-cbcac14909b2",
"metadata": {},
"outputs": [],
"source": [
"length(eng2de)"
]
},
{
"cell_type": "markdown",
"id": "b4ac7b46-9fc0-47fd-b6c0-c97791517b51",
"metadata": {},
"source": [
"Die Funktion `keys` gibt eine Sammlung mit den Schlüsseln des Wörterbuchs zurück:"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "3236f973-d3fa-42ba-a1c0-41e0002446cb",
"metadata": {},
"outputs": [],
"source": [
"keys(eng2de)"
]
},
{
"cell_type": "markdown",
"id": "f9bc6fe5-3f7a-458e-942a-11bd3e83dcc6",
"metadata": {},
"source": [
"Nun kannst du den Operator `∈` verwenden, um zu sehen, ob etwas als Schlüssel im Wörterbuch vorhanden ist:"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "1fdeb10c-0d84-4c22-b734-d68e2eafe375",
"metadata": {},
"outputs": [],
"source": [
"@show \"one\" ∈ keys(eng2de)\n",
"@show \"four\" ∈ keys(eng2de);"
]
},
{
"cell_type": "markdown",
"id": "864d141e-6cd2-45e6-95fe-e84cca35cf70",
"metadata": {},
"source": [
"Um zu überprüfen, ob etwas als Wert in einem Wörterbuch vorhanden ist, kannst du die Funktion `values` verwenden, die eine Sammlung von Werten zurückgibt, und dann den Operator `∈` verwenden:"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "ab90dad1-61d0-4dbd-b829-a354c13fcb71",
"metadata": {},
"outputs": [],
"source": [
"@show \"ein\" ∈ values(eng2de)\n",
"@show \"vier\" ∈ values(eng2de);"
]
},
{
"cell_type": "markdown",
"id": "5b255b31-8aca-49b4-a95f-302c59baa975",
"metadata": {},
"source": [
"### Schleifen über Zuordnungstabellen\n",
"\n",
"Du kannst die Schlüssel der Zuordnungstabelle in einer `for`-Schleife durchlaufen."
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "e7062d3f-2125-4ecd-ab13-d04944a71fe1",
"metadata": {},
"outputs": [],
"source": [
"function print_dictionary(dic)\n",
" for key in keys(dic)\n",
" println(key, \" \", dic[key])\n",
" end\n",
"end\n",
"\n",
"print_dictionary(eng2de)"
]
},
{
"cell_type": "markdown",
"id": "a6b3285a-58f6-41b6-8755-2732a367424e",
"metadata": {},
"source": [
"Auch hier sind die Schlüssel in keiner bestimmten Reihenfolge. Um die Schlüssel in alphabetisch sortierter Reihenfolge zu durchlaufen, kannst du `sort` und `collect` kombinieren:"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "6d52aeba-3c7e-405c-bcfe-d76e37e94f51",
"metadata": {},
"outputs": [],
"source": [
"function print_dictionary_sorted(dic)\n",
" for key in sort(collect(keys(dic)))\n",
" println(key, \" \", dic[key])\n",
" end\n",
"end\n",
"\n",
"print_dictionary_sorted(eng2de)"
]
},
{
"cell_type": "markdown",
"id": "964165ee-4d0d-4122-b6b2-d3926101cab2",
"metadata": {},
"source": [
"### Übung\n",
" - Schreibe eine Funktion, die bei Eingabe eines Strings zählt, wie oft jeder Buchstabe vorkommt. Verwende ein Wörterbuch."
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "aba1978b-e675-48a7-89aa-75553db70537",
"metadata": {},
"outputs": [],
"source": []
},
{
"cell_type": "markdown",
"id": "0acaa2f9-372c-4a3e-be01-27cfe2853fdb",
"metadata": {},
"source": [
"## Tupel und benannte Tupel"
]
},
{
"cell_type": "markdown",
"id": "68ea4303-ce3c-4647-8e9f-89afd0a6a670",
"metadata": {},
"source": [
"Ein Tupel ist eine Sequenz von Werten. Die Werte können beliebigen Typs sein und sie werden durch Ganzzahlen indexiert. Die Tupel sind unveränderlich und jedes Element kann seinen eigenen Typ haben."
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "8502cf2e-8bea-4707-811c-f4e6e1de3c8f",
"metadata": {},
"outputs": [],
"source": [
"t = 'a', 'b', 'c', 'd', 'e'"
]
},
{
"cell_type": "markdown",
"id": "2632b11b-de4b-4f21-bcc6-06a32517c121",
"metadata": {},
"source": [
"Obwohl es nicht notwendig ist, ist es üblich, Tupel in Klammern zu setzen:"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "bfa31b99-bf9d-49bb-babc-fe8a65248429",
"metadata": {},
"outputs": [],
"source": [
"t = ('a', 'b', 'c', 'd', 'e')"
]
},
{
"cell_type": "markdown",
"id": "86fa8b6b-bbbf-4c9c-a5c0-63b00b2c59cb",
"metadata": {},
"source": [
"Um ein Tupel mit einem einzigen Element zu erstellen, musst du ein abschließendes Komma setzen:"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "cb1bea15-2b80-43b2-be8e-27507d550fca",
"metadata": {},
"outputs": [],
"source": [
"t1 = ('a',)\n",
"@show typeof(t1)\n",
"\n",
"t2 = ('a')\n",
"@show typeof(t2);"
]
},
{
"cell_type": "markdown",
"id": "d6f34b5b-2aaa-49db-b2a8-d5891fe33ee1",
"metadata": {},
"source": [
"Eine andere Möglichkeit, ein Tupel zu erstellen, ist die integrierte Funktion `tuple`. Ohne Argument erstellt sie ein leeres Tupel:"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "952acafe-ca5d-4b58-8969-224b0fe3a28f",
"metadata": {},
"outputs": [],
"source": [
"t3 = tuple()\n",
"@show typeof(t3)\n",
"\n",
"t4 = tuple(1, 'a', π, 12.0)\n",
"@show typeof(t4);"
]
},
{
"cell_type": "markdown",
"id": "efc3d061-7e93-4863-a802-a7cdb869af7e",
"metadata": {},
"source": [
"Mit dem eckigen Klammern-Operator ist es möglich, auf ein Element zuzugreifen. Beachte, dass in Julia das erste Element, anders als in anderen Programmiersprachen (zum Beispiel C, C++, Python), mit 1 indexiert wird."
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "9817c28f-428d-4d5b-8384-608f9b4d763d",
"metadata": {},
"outputs": [],
"source": [
"t4[2]"
]
},
{
"cell_type": "markdown",
"id": "17a0f0ee-e03f-4669-a5f1-889d875577d9",
"metadata": {},
"source": [
"Es ist auch möglich, mehrere Elemente mithilfe von Slices zu erhalten."
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "c48ac07f-3a4e-4bb8-9551-2973ea8d2f9c",
"metadata": {},
"outputs": [],
"source": [
"@show t4[1:3];"
]
},
{
"cell_type": "markdown",
"id": "d0f25a1f-0a4c-4ab4-a339-f5ba64806681",
"metadata": {},
"source": [
"Aber wenn du versuchst, eines der Elemente des Tupels zu ändern, tritt ein Fehler auf:"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "cfc94cc9-832c-45d2-85b8-aaf789f33f6a",
"metadata": {},
"outputs": [],
"source": [
"t4[2] = 'b'"
]
},
{
"cell_type": "markdown",
"id": "d956168e-36e4-4abb-a3d6-3c53a857e78d",
"metadata": {},
"source": [
"Da Tupel unveränderlich sind, kannst du die Elemente nicht ändern."
]
},
{
"cell_type": "markdown",
"id": "6a115819-54fd-4550-b097-59fea3ecb523",
"metadata": {},
"source": [
"Genau genommen kann eine Funktion nur einen Wert zurückgeben, aber wenn der Wert ein Tupel ist, hat dies den gleichen Effekt wie das Zurückgeben mehrerer Werte. Zum Beispiel, wenn du zwei Ganzzahlen teilen und den Quotienten und den Rest berechnen möchtest, ist es ineffizient, zuerst x ÷ y und dann x % y zu berechnen. Es ist besser, beides gleichzeitig zu berechnen.\n",
"\n",
"Die integrierte Funktion `divrem` nimmt zwei Argumente entgegen und gibt ein Tupel aus zwei Werten zurück, dem Quotienten und dem Rest. Du kannst das Ergebnis als Tupel speichern:"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "d1dde137-036f-4b93-8010-13b3e835ebd8",
"metadata": {},
"outputs": [],
"source": [
"t = divrem(7, 3)"
]
},
{
"cell_type": "markdown",
"id": "9cd48d9c-cf1d-4546-a2c0-4bb54a42e1e8",
"metadata": {},
"source": [
"Alternativ können wir die Zuweisung als Tupel verwenden, um die Elemente getrennt zu speichern:"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "c4969556-7945-43c9-8b96-1ea1032cbb3c",
"metadata": {},
"outputs": [],
"source": [
"q, r = divrem(7, 3)\n",
"\n",
"@show q r;"
]
},
{
"cell_type": "markdown",
"id": "e4177fab-a386-4274-8657-bbc6698ec1e5",
"metadata": {},
"source": [
"### Sammeln und verteilen\n",
"\n",
"Funktionen können eine variable Anzahl von Argumenten entgegennehmen. Ein Parametername, der mit `...` endet, sammelt die Argumente in ein Tupel. Zum Beispiel nimmt `printall` eine beliebige Anzahl von Argumenten entgegen und gibt sie nacheinander aus:"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "43f8c7ad-5ac2-425e-a50f-c45624bb3d8a",
"metadata": {},
"outputs": [],
"source": [
"function printall(args...)\n",
" for arg in args\n",
" println(arg)\n",
" end\n",
"end\n",
"\n",
"printall(1)\n",
"printall(1, 2.0, '3')"
]
},
{
"cell_type": "markdown",
"id": "28b3cd91-bf05-43bc-a253-2bee077a1df1",
"metadata": {},
"source": [
"Das Gegenstück zu \"sammeln\" ist \"verteilen\". Wenn du eine Sequenz von Werten hast und sie als mehrere Argumente an eine Funktion übergeben möchtest, kannst du den Operator `...` verwenden. Zum Beispiel nimmt `divrem` genau zwei Argumente an; es funktioniert nicht mit einem Tupel:"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "bf55fc10-8432-41e3-bd6c-2244c8c96513",
"metadata": {},
"outputs": [],
"source": [
"t = (7, 3)\n",
"divrem(t)"
]
},
{
"cell_type": "markdown",
"id": "db3cd00d-d41c-4f68-95d4-c4e5b283734a",
"metadata": {},
"source": [
"Wenn du aber das Tupel verteilst, funktioniert es:"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "e7df53bc-6b2d-4501-9ca8-61e3cb79eb1a",
"metadata": {},
"outputs": [],
"source": [
"t = (7, 3)\n",
"divrem(t...)"
]
},
{
"cell_type": "markdown",
"id": "dad5d7bf-0dd5-450b-90f3-7768f2d91c18",
"metadata": {},
"source": [
"### Zuordnungstabellen und Tupel"
]
},
{
"cell_type": "markdown",
"id": "3a38cabd-b056-4140-a75c-e0fa383a2a74",
"metadata": {},
"source": [
"Zuordnungstabellen können als Iteratoren verwendet werden, um die Schlüssel-Wert-Paare zu durchlaufen. Du kannst es in einer `for`-Schleife wie folgt verwenden:\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "ed483334-36d0-4700-b178-cd9dc5f12189",
"metadata": {},
"outputs": [],
"source": [
"d = Dict('a'=>1, 'b'=>2, 'c'=>3);\n",
"for (key, value) in d\n",
" println(key, \" \", value)\n",
"end"
]
},
{
"cell_type": "markdown",
"id": "5a012731-cf42-487b-9d02-4a4b1d0475c0",
"metadata": {},
"source": [
"Es ist üblich, Tupel als Schlüssel in Zuordnungstabellen zu verwenden. Zum Beispiel könnte ein Telefonverzeichnis Nachname-Vorname-Paare auf Telefonnummern abbilden. Angenommen, wir haben last, first und number definiert, könnten wir schreiben:"
]
},
{
"cell_type": "markdown",
"id": "e5462d35-fa68-42d8-ba00-c5dd1747c557",
"metadata": {},
"source": [
"`directory[last, first] = number`"
]
},
{
"cell_type": "markdown",
"id": "7bd0a942-de38-4c9e-96df-a786fa814254",
"metadata": {},
"source": [
"### Benannte Tupel"
]
},
{
"cell_type": "markdown",
"id": "2284786f-cb8b-4173-944a-e50d79ceca2d",
"metadata": {},
"source": [
"Benannte Tupel sind ein besonderer Tupeltyp in Julia, bei dem jedem Element ein spezifischer Name zugeordnet ist. Dies ermöglicht es dir, auf Elemente anhand ihrer Namen zuzugreifen, was den Code lesbarer und selbsterklärender macht."
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "78fb5959-407a-4128-9287-bb9d455f22dc",
"metadata": {},
"outputs": [],
"source": [
"person = (name = \"Michele\", age = 28, city = \"Stuttgart\")"
]
},
{
"cell_type": "markdown",
"id": "01e99a5c-f331-4ad6-95e0-e403b8ad327b",
"metadata": {},
"source": [
"Benannte Tupel sind unveränderlich, aber es ist möglich, den Inhalt zu aktualisieren oder neue Elemente hinzuzufügen, indem eine neue Version erstellt wird. Zum Beispiel:"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "ecd22e07-5ba3-4f11-bc77-d967a6fc3fe8",
"metadata": {},
"outputs": [],
"source": [
"updated_person = (person..., age = 29)\n",
"updated_person = (person..., number = \"0711 ...\")"
]
},
{
"cell_type": "markdown",
"id": "70061322-9435-496a-ac0d-c026f41e5936",
"metadata": {},
"source": [
"### Übung\n",
" - Schreibe eine Funktion `swap_first_last`, die ein Tupel entgegennimmt und ein neues Tupel zurückgibt, bei dem das erste und das letzte Element vertauscht sind. Tipp: Verwende eine Kombination des `...`-Operators und des Slices `[2:end-1]`.\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "3721b654-b182-426d-b799-a69e0b53a550",
"metadata": {},
"outputs": [],
"source": []
},
{
"cell_type": "markdown",
"id": "5629a477-5d19-43a2-8bb4-e10b10f2ffde",
"metadata": {},
"source": [
"## Objekte und Strukturen"
]
},
{
"cell_type": "markdown",
"id": "18412e4f-e5fb-4a92-a083-ac2e5b58cde3",
"metadata": {},
"source": [
"Zu diesem Zeitpunkt weißt du bereits, wie man Funktionen verwendet, um Code zu organisieren, und integrierte Typen, um Daten zu organisieren. Der nächste Schritt ist zu lernen, wie man eigene Typen erstellt, um sowohl Code als auch Daten zu organisieren.\n",
"\n",
"Wir haben viele der integrierten Typen von Julia verwendet; jetzt werden wir einen neuen Typ definieren. Als Beispiel werden wir einen Typ namens Punkt erstellen, der einen Punkt im zweidimensionalen Raum repräsentiert.\n",
"\n",
"Es gibt verschiedene Möglichkeiten, wie wir Punkte in Julia darstellen könnten:\n",
" - Wir könnten die Koordinaten separat in zwei Variablen, x und y, speichern.\n",
" - Wir könnten die Koordinaten als Elemente in einem Tupel speichern.\n",
" - Wir könnten einen neuen Typ erstellen, um Punkte als Objekte darzustellen.\n",
"\n",
"Hier werden wir die dritte Option untersuchen."
]
},
{
"cell_type": "markdown",
"id": "270c430a-4157-4e54-bb27-660c4028e5c6",
"metadata": {},
"source": [
"Ein vom Programmierer definierter zusammengesetzter Typ wird auch als Struktur (engl. struct) bezeichnet. Die Strukturdefinition für einen Punkt sieht so aus:"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "c2e3f646-2d82-46a4-a265-01b919412c6d",
"metadata": {},
"outputs": [],
"source": [
"struct Point\n",
" x::Real\n",
" y::Real\n",
"end"
]
},
{
"cell_type": "markdown",
"id": "6b99219f-70ba-41a7-8c52-e3803a360087",
"metadata": {},
"source": [
"Die Kopfzeile zeigt an, dass die neue Struktur Point genannt wird. Der Körper definiert die Attribute oder Felder der Struktur. Die Point-Struktur hat zwei Felder: x und y.\n",
"\n",
"Eine Struktur ist wie eine Fabrik zur Erzeugung von Objekten. Um einen Punkt zu erstellen, rufst du Point auf, als wäre es eine Funktion, und übergibst die Werte der Felder als Argumente. Wenn Point als Funktion verwendet wird, wird dies Konstruktor genannt. Der Konstruktor gibt eine Instanz des Objekts zurück.\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "197339a2-0d8b-4882-bab2-80ba56410f21",
"metadata": {},
"outputs": [],
"source": [
"p = Point(3.0, 4.0)\n",
"@show p;"
]
},
{
"cell_type": "markdown",
"id": "e35cbbf5-491e-44ff-b2c0-0cf6a8853083",
"metadata": {},
"source": [
"Die Typangabe (`::Real`) ist optional, kann aber hilfreich sein, um die korrekte Verwendung der Struktur zu erzwingen."
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "849f1460-d228-4b22-a797-1077d75d1a2d",
"metadata": {},
"outputs": [],
"source": [
"p = Point(\"a\", 4.0)"
]
},
{
"cell_type": "markdown",
"id": "b86615f4-592f-41f7-b9d1-f5d1573cfd86",
"metadata": {},
"source": [
"Die Werte der Felder können mithilfe des `.` Operators abgerufen werden."
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "00d090e4-d74d-4fad-ae8b-369c1046e48b",
"metadata": {},
"outputs": [],
"source": [
"@show p.x p.y;\n",
"@show distance = sqrt(p.x^2 + p.y^2);"
]
},
{
"cell_type": "markdown",
"id": "7c6baa9c-56e2-4595-9e6b-c798d9cea002",
"metadata": {},
"source": [
"Die Werte der Felder können mithilfe des `.` Operators abgerufen werden."
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "91bd6248-a358-43e3-90cc-6b8398445aaf",
"metadata": {},
"outputs": [],
"source": [
"p.x = 2"
]
},
{
"cell_type": "markdown",
"id": "48b46edb-b8fd-46ca-a2ec-4aa0acbf6c01",
"metadata": {},
"source": [
"Wenn erforderlich, können veränderbare zusammengesetzte Typen mit dem Schlüsselwort `mutable struct` deklariert werden. Hier ist die Definition eines veränderbaren Punkts:\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "804c2c32-7a84-4261-88d8-1bad6ba5e18c",
"metadata": {},
"outputs": [],
"source": [
"mutable struct MPoint\n",
" x::Real\n",
" y::Real\n",
"end\n",
"p = MPoint(3.0, 4.0)\n",
"p.x = 2.0\n",
"@show p;"
]
},
{
"cell_type": "markdown",
"id": "06f44f63-bf8e-45f5-9495-006e21fc7560",
"metadata": {},
"source": [
"Eine dritte Option ist es, einige Felder einer unveränderlichen Struktur als veränderbar zu definieren. Zum Beispiel kann eine Zuordnungstabellen innerhalb einer unveränderlichen Struktur geändert werden."
"Du kannst eine Instanz auf die übliche Weise als Argument übergeben. Zum Beispiel:"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "e6113221-0c03-484a-99e0-f7d4fab70f03",
"metadata": {},
"outputs": [],
"source": [
"function print_point(p)\n",
" println(\"($(p.x), $(p.y))\")\n",
"end\n",
"print_point(p)"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "d5c6fa9e-c31d-4aff-a3fd-db3449904519",
"metadata": {},
"outputs": [],
"source": [
"function print_book(book)\n",
" println(\"Title: $(book.title)\")\n",
" println(\"Author: $(book.author)\")\n",
" available = book.properties[\"available\"]\n",
" println(\"Available: $(available)\")\n",
"end\n",
"print_book(book)"
]
},
{
"cell_type": "markdown",
"id": "916562a2-3402-41bb-b15d-01333915df99",
"metadata": {},
"source": [
"Funktionen können Instanzen als Rückgabewerte zurückgeben."
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "9c57c9f7-c4fd-4263-a55f-62f123aaaa8a",
"metadata": {},
"outputs": [],
"source": [
"function find_center(point1, point2)\n",
" x = (point1.x + point2.x)/2.0\n",
" y = (point1.y + point2.y)/2.0\n",
" return Point(x, y)\n",
"end\n",
"\n",
"point1 = Point(0.0, 0.0)\n",
"point2 = Point(10.0, 10.0)\n",
"\n",
"@show find_center(point1, point2);"
]
},
{
"cell_type": "markdown",
"id": "4afa9ca0-e6cd-4713-8caa-689c9dfdee42",
"metadata": {},
"source": [
"### Übung\n",
"\n",
" - Schreibe eine Funktion namens `point_distance`, die zwei Punkte als Argumente entgegennimmt und die euklidische Distanz zwischen ihnen zurückgibt."
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "b5957a05-e396-41c3-9b8f-f14f089115ab",
"metadata": {},
"outputs": [],
"source": []
},
{
"cell_type": "markdown",
"id": "eecb6161-768e-4ae6-a600-d892894f8824",
"metadata": {},
"source": [
"### Referenzen und Werte\n",
"\n",
"Jedes Objekt (Instanz einer Struktur) wird an einer bestimmten Speicheradresse gespeichert. Der Operator `===` überprüft, ob zwei Variablen auf die gleiche Speicheradresse des Objekts zeigen. Zum Beispiel"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "ed713d91-17f4-4010-a7d3-d39826cffc97",
"metadata": {},
"outputs": [],
"source": [
"book_copy = book\n",
"@show book_copy === book;"
]
},
{
"cell_type": "markdown",
"id": "582e0ac0-2304-429e-a678-c37ab157d5d6",
"metadata": {},
"source": [
"Das bedeutet, dass jede Änderung, die an `book_copy` vorgenommen wird, auch `book` modifiziert."
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "a9c46bac-d234-4616-a604-63bdf03b1393",
"metadata": {},
"outputs": [],
"source": [
"book.properties[\"available\"] = true\n",
"book_copy.properties[\"available\"] = false\n",
"\n",
"@show book;"
]
},
{
"cell_type": "markdown",
"id": "79339467-8994-4a5c-8987-3420d14cb290",
"metadata": {},
"source": [
"Wenn ein neues, eindeutiges Objekt benötigt wird (mit anderen Worten, eine Kopie nach Wert), können wir die Funktion `deepcopy` verwenden."
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "c36d23c8-2292-4568-aef4-7416c6f3e538",
"metadata": {},
"outputs": [],
"source": [
"book_copy = deepcopy(book)\n",
"@show book_copy === book;"
]
},
{
"cell_type": "markdown",
"id": "4cc4c096-d856-4d43-8808-94edcd3ad906",
"metadata": {},
"source": [
"Schließlich entspricht der `==` Operator zwischen Strukturen standardmäßig dem `===` Operator, da Julia nicht weiß, wie benutzerdefinierte Strukturen verglichen werden sollen. Es ist jedoch immer möglich, den `==` Operator für unsere benutzerdefinierten Typen neu zu implementieren."
"Damit können wir vergleichen, ob zwei Bücher gleich sind, auch wenn es sich um zwei unterschiedliche Instanzen desselben Typs handelt."
]
},
{
"cell_type": "markdown",
"id": "4e457ee2-813a-42a8-8ee0-600bb4b50812",
"metadata": {},
"source": [
"## Reine und unreine Funktionen"
]
},
{
"cell_type": "markdown",
"id": "fb008ae0-a457-45db-8f77-425d56e95b30",
"metadata": {},
"source": [
"Wir haben gesehen, dass es ziemlich praktisch ist, Objekte an Funktionen zu übergeben. Bisher haben wir Funktionen gesehen, die den Inhalt ausgeben oder ein neues Objekt erstellen. Keine dieser Funktionen ändert den Inhalt der Eingabeobjekte.\n",
" - Eine Funktion, die die Eingabe nicht ändert, wird auch als **rein** bezeichnet.\n",
" - Eine Funktion, die die Eingabe ändert, wird als **unrein** bezeichnet. In Julia werden unreine Funktionen durch Hinzufügen eines `!` als letztem Zeichen des Namens hervorgehoben.\n",
"\n",
"Betrachten wir eine modifizierte Version des Buchs als Beispiel."
" - Schreibe eine unreine Funktion `sell_book!`, um das Buchobjekt zu aktualisieren, wenn wir ein Exemplar verkaufen. Die Funktion sollte eine Meldung ausgeben, wenn keine Exemplare verfügbar sind."
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "6e79fe95-767d-4b3e-af58-623db6dc0712",
"metadata": {},
"outputs": [],
"source": []
},
{
"cell_type": "markdown",
"id": "d5bb0b58-e85e-4edb-80b6-19d2c7d49ac3",
"metadata": {},
"source": [
"## Weitere Übungen"
]
},
{
"cell_type": "markdown",
"id": "15fd6da2-f862-40ef-a58d-5e7be488565f",
"metadata": {},
"source": [
"- Erstelle eine Zeitstruktur, die die Uhrzeit des Tages (Stunde, Minute und Sekunde) speichert. Schreibe eine Funktion, die als Eingabe das Zeitobjekt nimmt und es ausgibt. Schreibe dann eine Funktion, die zwei Zeitobjekte zusammenzählt und ein neues Zeitobjekt zurückgibt. Wenn die Summe mehr als 24 Stunden beträgt, sollte sie wie am nächsten Tag zurückgesetzt werden."
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "5335e035-62ee-4d0f-acfc-a9e24ac26d99",
"metadata": {},
"outputs": [],
"source": []
},
{
"cell_type": "markdown",
"id": "2e0fde12-1214-4709-a7cb-5ebfaa0af17a",
"metadata": {},
"source": [
"- Erstelle eine benutzerdefinierte Version der `==` Funktion, die überprüft, ob zwei Zeitobjekte identisch sind."
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "966d4db1-c95a-4531-8209-a869519599ea",
"metadata": {},
"outputs": [],
"source": []
},
{
"cell_type": "markdown",
"id": "4e8c3c1e-14fb-439f-97c7-076196f7e5b4",
"metadata": {},
"source": [
"- Erstelle eine benutzerdefinierte Version der `<` und `<=` Funktionen für Zeitobjekte."
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "c9b76667-731b-4ec2-aaf4-e94496f65b1a",
"metadata": {},
"outputs": [],
"source": []
},
{
"cell_type": "markdown",
"id": "b79aad8c-647e-46d0-9b04-6a77cd219953",
"metadata": {},
"source": [
"- Erstelle eine benutzerdefinierte Version der `>` und `>=` Funktionen für Zeitobjekte."
Eine Zuordnungstabelle enthält eine Sammlung von Indizes, die als Schlüssel bezeichnet werden, und eine Sammlung von Werten. Jeder Schlüssel ist mit einem einzigen Wert verbunden. Die Zuordnung eines Schlüssels und eines Wertes wird als Schlüssel-Wert-Paar oder manchmal als Element bezeichnet.
Die Funktion `Dict` erstellt ein neues Wörterbuch ohne Elemente. Da `Dict` der Name einer integrierten Funktion ist, sollte man vermeiden ihn als Variablenname zu verwenden.
Wörterbücher sind veränderbar, was bedeutet, dass es immer möglich ist, ihre Elemente zu ändern, neue Schlüssel-Wert-Paare hinzuzufügen oder bestehende zu entfernen.
Im Allgemeinen ist die Reihenfolge der Schlüssel-Wert-Paare unvorhersehbar. Dies ist jedoch nicht wichtig, da die Werte immer mithilfe der Schlüssel abgerufen werden.
Um zu überprüfen, ob etwas als Wert in einem Wörterbuch vorhanden ist, kannst du die Funktion `values` verwenden, die eine Sammlung von Werten zurückgibt, und dann den Operator `∈` verwenden:
Auch hier sind die Schlüssel in keiner bestimmten Reihenfolge. Um die Schlüssel in alphabetisch sortierter Reihenfolge zu durchlaufen, kannst du `sort` und `collect` kombinieren:
Mit dem eckigen Klammern-Operator ist es möglich, auf ein Element zuzugreifen. Beachte, dass in Julia das erste Element, anders als in anderen Programmiersprachen (zum Beispiel C, C++, Python), mit 1 indexiert wird.
Genau genommen kann eine Funktion nur einen Wert zurückgeben, aber wenn der Wert ein Tupel ist, hat dies den gleichen Effekt wie das Zurückgeben mehrerer Werte. Zum Beispiel, wenn du zwei Ganzzahlen teilen und den Quotienten und den Rest berechnen möchtest, ist es ineffizient, zuerst x ÷ y und dann x % y zu berechnen. Es ist besser, beides gleichzeitig zu berechnen.
Die integrierte Funktion `divrem` nimmt zwei Argumente entgegen und gibt ein Tupel aus zwei Werten zurück, dem Quotienten und dem Rest. Du kannst das Ergebnis als Tupel speichern:
Funktionen können eine variable Anzahl von Argumenten entgegennehmen. Ein Parametername, der mit `...` endet, sammelt die Argumente in ein Tupel. Zum Beispiel nimmt `printall` eine beliebige Anzahl von Argumenten entgegen und gibt sie nacheinander aus:
Das Gegenstück zu "sammeln" ist "verteilen". Wenn du eine Sequenz von Werten hast und sie als mehrere Argumente an eine Funktion übergeben möchtest, kannst du den Operator `...` verwenden. Zum Beispiel nimmt `divrem` genau zwei Argumente an; es funktioniert nicht mit einem Tupel:
Zuordnungstabellen können als Iteratoren verwendet werden, um die Schlüssel-Wert-Paare zu durchlaufen. Du kannst es in einer `for`-Schleife wie folgt verwenden:
Es ist üblich, Tupel als Schlüssel in Zuordnungstabellen zu verwenden. Zum Beispiel könnte ein Telefonverzeichnis Nachname-Vorname-Paare auf Telefonnummern abbilden. Angenommen, wir haben last, first und number definiert, könnten wir schreiben:
Benannte Tupel sind ein besonderer Tupeltyp in Julia, bei dem jedem Element ein spezifischer Name zugeordnet ist. Dies ermöglicht es dir, auf Elemente anhand ihrer Namen zuzugreifen, was den Code lesbarer und selbsterklärender macht.
Benannte Tupel sind unveränderlich, aber es ist möglich, den Inhalt zu aktualisieren oder neue Elemente hinzuzufügen, indem eine neue Version erstellt wird. Zum Beispiel:
- Schreibe eine Funktion `swap_first_last`, die ein Tupel entgegennimmt und ein neues Tupel zurückgibt, bei dem das erste und das letzte Element vertauscht sind. Tipp: Verwende eine Kombination des `...`-Operators und des Slices `[2:end-1]`.
Zu diesem Zeitpunkt weißt du bereits, wie man Funktionen verwendet, um Code zu organisieren, und integrierte Typen, um Daten zu organisieren. Der nächste Schritt ist zu lernen, wie man eigene Typen erstellt, um sowohl Code als auch Daten zu organisieren.
Wir haben viele der integrierten Typen von Julia verwendet; jetzt werden wir einen neuen Typ definieren. Als Beispiel werden wir einen Typ namens Punkt erstellen, der einen Punkt im zweidimensionalen Raum repräsentiert.
Es gibt verschiedene Möglichkeiten, wie wir Punkte in Julia darstellen könnten:
- Wir könnten die Koordinaten separat in zwei Variablen, x und y, speichern.
- Wir könnten die Koordinaten als Elemente in einem Tupel speichern.
- Wir könnten einen neuen Typ erstellen, um Punkte als Objekte darzustellen.
Ein vom Programmierer definierter zusammengesetzter Typ wird auch als Struktur (engl. struct) bezeichnet. Die Strukturdefinition für einen Punkt sieht so aus:
Die Kopfzeile zeigt an, dass die neue Struktur Point genannt wird. Der Körper definiert die Attribute oder Felder der Struktur. Die Point-Struktur hat zwei Felder: x und y.
Eine Struktur ist wie eine Fabrik zur Erzeugung von Objekten. Um einen Punkt zu erstellen, rufst du Point auf, als wäre es eine Funktion, und übergibst die Werte der Felder als Argumente. Wenn Point als Funktion verwendet wird, wird dies Konstruktor genannt. Der Konstruktor gibt eine Instanz des Objekts zurück.
Wenn erforderlich, können veränderbare zusammengesetzte Typen mit dem Schlüsselwort `mutable struct` deklariert werden. Hier ist die Definition eines veränderbaren Punkts:
Eine dritte Option ist es, einige Felder einer unveränderlichen Struktur als veränderbar zu definieren. Zum Beispiel kann eine Zuordnungstabellen innerhalb einer unveränderlichen Struktur geändert werden.
Jedes Objekt (Instanz einer Struktur) wird an einer bestimmten Speicheradresse gespeichert. Der Operator `===` überprüft, ob zwei Variablen auf die gleiche Speicheradresse des Objekts zeigen. Zum Beispiel
Schließlich entspricht der `==` Operator zwischen Strukturen standardmäßig dem `===` Operator, da Julia nicht weiß, wie benutzerdefinierte Strukturen verglichen werden sollen. Es ist jedoch immer möglich, den `==` Operator für unsere benutzerdefinierten Typen neu zu implementieren.
Wir haben gesehen, dass es ziemlich praktisch ist, Objekte an Funktionen zu übergeben. Bisher haben wir Funktionen gesehen, die den Inhalt ausgeben oder ein neues Objekt erstellen. Keine dieser Funktionen ändert den Inhalt der Eingabeobjekte.
- Eine Funktion, die die Eingabe nicht ändert, wird auch als **rein** bezeichnet.
- Eine Funktion, die die Eingabe ändert, wird als **unrein** bezeichnet. In Julia werden unreine Funktionen durch Hinzufügen eines `!` als letztem Zeichen des Namens hervorgehoben.
Betrachten wir eine modifizierte Version des Buchs als Beispiel.
- Schreibe eine unreine Funktion `sell_book!`, um das Buchobjekt zu aktualisieren, wenn wir ein Exemplar verkaufen. Die Funktion sollte eine Meldung ausgeben, wenn keine Exemplare verfügbar sind.
- Erstelle eine Zeitstruktur, die die Uhrzeit des Tages (Stunde, Minute und Sekunde) speichert. Schreibe eine Funktion, die als Eingabe das Zeitobjekt nimmt und es ausgibt. Schreibe dann eine Funktion, die zwei Zeitobjekte zusammenzählt und ein neues Zeitobjekt zurückgibt. Wenn die Summe mehr als 24 Stunden beträgt, sollte sie wie am nächsten Tag zurückgesetzt werden.