added tut01
@ -1,99 +1,286 @@
|
||||
# Tutorium 01 - 20.10.2023
|
||||
---
|
||||
marp: true
|
||||
paginate: true
|
||||
theme: rose-pine
|
||||
footer: EidP 2024 - Nils Pukropp - https://git.narl.io/nvrl/eidp-2024
|
||||
style: ".columns {\r display: grid;\r grid-template-columns: repeat(2, minmax(0, 1fr));\r gap: 1rem;\r }"
|
||||
---
|
||||
# Tutorium 01 - 2024-10-17
|
||||
Vorstellen, Orga, Zusammenfassung, Installation von WSL/VS Code
|
||||
|
||||
## Today
|
||||
---
|
||||
# Über mich
|
||||
|
||||
* Vorstellen
|
||||
* Zusammenfassung Vorlesung
|
||||
* Übungsblatt 01
|
||||
* Installieren der benötigten Software
|
||||
<div class="columns">
|
||||
|
||||
<div>
|
||||
|
||||
## About me
|
||||
|
||||
* Nils Pukropp
|
||||
* 3 Semester Informatik B.Sc.
|
||||
* [nils@narl.io](mailto:nils@narl.io)
|
||||
* Discord: [.narl](https://discord.com/users/208979474988007425)
|
||||
* Telegram: [@narl_np](https://t.me/narl_np)
|
||||
* [Feedback](https://s.narl.io/s/Feedback-Tutorium-01)
|
||||
|
||||
<img src="../../src/img/mailto.png" height="200">
|
||||
<img src="../../src/img/discord.png" height="200">
|
||||
<img src="../../src/img/telegram.png" height="200">
|
||||
<img src="../../src/img/feedback-google-forms.png" height="200">
|
||||
<img src="./src/tutorium-01.png" height="200">
|
||||
* Informatik B.Sc. 5. Semester
|
||||
* EidP Tutor seit zwei Semestern
|
||||
* Gerne einfach "Du"
|
||||
|
||||
## Zusammenfassung Vorlesung
|
||||
|
||||
### Orga
|
||||
|
||||
* 14 Blätter jeden Dienstag auf der [Homepage](https://proglang.informatik.uni-freiburg.de/teaching/info1/2023/)
|
||||
* Abgabe im [Git](https://git.laurel.informatik.uni-freiburg.de/), Montags 9:00
|
||||
* Ihr braucht 50% der 326 Punkte
|
||||
* 2x Vorrechnen
|
||||
* Muss nicht korrekt sein
|
||||
* Wollen sehen dass ihr eure Aufgaben selber bearbeitet
|
||||
* Ich werde euch fragen, wenn ihr gute Abgaben hattet
|
||||
* Punkte für Anwesenheit im Tutorat
|
||||
* Anwesenheit über [QR-Code](https://auth.laurel.informatik.uni-freiburg.de/) (ab nächste Woche)
|
||||
|
||||
### Python-Shell
|
||||
|
||||
* Python-Shell bietet einen interaktiven Modus
|
||||
* Schnell Programme testen/kleinere Programme schreiben
|
||||
|
||||
### Zahlen
|
||||
|
||||
* `int` (Ganzzahlen)
|
||||
* `float` (Kommazahlen)
|
||||
* Grunderechenarten `+`, `-`, `*`, `/`, `//`
|
||||
* Potenz `**`
|
||||
* Modulo `%`
|
||||
* `float` -> `int` (möglicher) Informationsverlust
|
||||
* `int` -> `float` kein Informationsverlust
|
||||
* `float` sind etwas komplizierter als `int`
|
||||
</div>
|
||||
<div>
|
||||
|
||||
```py
|
||||
>>> 0.1 + 0.1 + 0.1
|
||||
?
|
||||
print("Hello", "Tutorium", "2024!")
|
||||
>>> "Hello Tutorium 2024!"
|
||||
```
|
||||
|
||||
### Git
|
||||
</div>
|
||||
</div>
|
||||
|
||||
* Git dient der Versionskontrolle
|
||||
* Wir benutzen [Gitea](https://github.com/go-gitea/gitea) als Git-Service
|
||||
* Remote Repositories
|
||||
* Weboberfläche zum offnen im Browser
|
||||
* Quasi eine Cloud für Code
|
||||
* Weitere Services wie Github, Gitlab, ...
|
||||
---
|
||||
|
||||
### Git - the nerdy way
|
||||
# Wie wird das Tutorium ablaufen?
|
||||
|
||||
#### Vorteile
|
||||
* Vorstellen des letzten Blatt
|
||||
* Wichtiges aus Vorlesung/für nächstes Blatt
|
||||
* Fragen zum nächsten Blatt
|
||||
* Allgemeine Fragen zur Vorlesung
|
||||
* Am Ende QR-Code für Anwesenheit
|
||||
* Ihr könnt mir gerne am Ende privat noch Fragen über die Vorlesung oder das Studium stellen
|
||||
|
||||
[Anleitung von Dani](https://git.danielmironov.dev/mironov/eidp-tutorat)
|
||||
---
|
||||
|
||||
* Man lernt umgang mit dem Terminal
|
||||
* Es ist deutlich schneller nach Eingewöhnung
|
||||
* Etwas unintuitiv wenn man sich mit dem Terminal nicht auskennt
|
||||
# Kontakt
|
||||
|
||||
## [Übungsblatt 01](https://proglang.informatik.uni-freiburg.de/teaching/info1/2023/exercise/sheet01.pdf)
|
||||
- Mail: [nils@narl.io](mailto:)
|
||||
- Tutorium-Files: [git.narl.io/nvrl/eidp-2024](https://git.narl.io/nvrl/eidp-2024)
|
||||
- Telegram: [@narl_np](https://t.me/narl_np)
|
||||
|
||||
* Abgabe 23.10.2023
|
||||
---
|
||||
|
||||
## Notes Tutorium
|
||||
# Orga
|
||||
Was müsst ihr machen/wissen
|
||||
|
||||
Die Windows `C:` Festplatte findet man in WSL unter `/mnt/c/`, andere Festplatten findet man analog dazu
|
||||
---
|
||||
|
||||
`D: -> /mnt/d/`
|
||||
...
|
||||
# Studienleistung bekommen
|
||||
|
||||
## Wichtige Links
|
||||
* Im HisInOne sich für dieses (oder ein anderes) Tutorium anmelden + Studienleistung (wird noch freigeschaltet)
|
||||
* 14 Blätter, jeden Dienstag auf der [EidP Website](https://proglang.github.io/teaching/24ws/eidp.html)
|
||||
* 2x Vorrechnen
|
||||
* Muss nicht korrekt sein
|
||||
* Ich werde gute Abgaben fragen
|
||||
* 3 Punkte für Anwesenheit
|
||||
* Ab nächstem Tutorium
|
||||
* Über [QR-Code](https://auth.laurel.informatik.uni-freiburg.de/)
|
||||
* Ihr braucht insgesamt mehr als 50% der Punkte (>50%)
|
||||
|
||||
* [Homepage EidP](https://proglang.informatik.uni-freiburg.de/teaching/info1/2023/)
|
||||
* [EidP-Git (Abgaben)](https://git.laurel.informatik.uni-freiburg.de/)
|
||||
* [QR-Code für Anwesenheit](https://auth.laurel.informatik.uni-freiburg.de/)
|
||||
* [Mein Git](https://git.narl.io/nvrl/eidp-2024)
|
||||
* [nils@narl.io](mailto:nils@narl.io)
|
||||
* [Discord](https://discord.com/users/208979474988007425)
|
||||
* [Telegram](https://t.me/narl_np)
|
||||
* [Feedback](https://s.narl.io/s/Feedback-Tutorium-01)
|
||||
---
|
||||
|
||||
# Prüfung
|
||||
|
||||
- Nach den Vorlesungen (Ende Februar/März)
|
||||
- Setzt die Studienleistung vorraus
|
||||
- Schwerere/Schwerste Klausur im ersten Semester
|
||||
|
||||
---
|
||||
|
||||
# Warum fällt vielen EidP so schwer?
|
||||
|
||||
- Zeitdruck in der Klausur
|
||||
- In der Studienleistung schummeln ist einfach, in der Klausur nicht
|
||||
- fehlende Übung im Programmieren
|
||||
- Nicht ausreichend mit den Themen befasst
|
||||
- Wahrscheinlich die Übungsblätter nicht vollständig selbst bearbeitet
|
||||
- Nachdem man die Studienleistung hat (>50% Punkte) aufgehört sich mit der Vorlesung zu beschäftigen
|
||||
|
||||
---
|
||||
|
||||
# ChatGPT
|
||||
|
||||
- Wir tolerieren den Gebrauch
|
||||
- **ABER**: Ihr müsst in der Klausur alles verstanden haben und genug Übung im Umgang mit Python haben
|
||||
- Ob ihr jetzt Stunden braucht um die Lösung von ChatGPT nachzuvollziehen oder euch in Stunden die Lösung selbst erarbeitet und dabei praktische Programmiererfahrung sammelt ist eure Entscheidung.
|
||||
- Aus eigenem Interesse würde ich generierte Stellen mit einem Kommentar versehen, damit ich einen Überblick habe welche Aufgaben schwerer fallen und wie viel ChatGPT verwendet wird
|
||||
|
||||
---
|
||||
|
||||
# Plagiat
|
||||
|
||||
- Wir tolerieren **kein** Plagiat
|
||||
- Nicht von anderen Abschreiben
|
||||
- Keine Lösungen aus dem Internet
|
||||
- Erster Plagiat: 0 Punkte
|
||||
- Zweiter Plagiat: Viel Erfolg im nächsten Jahr
|
||||
|
||||
---
|
||||
|
||||
# Abschließend
|
||||
|
||||
- Stellt Fragen im Tutorium
|
||||
- Im Tutorium für alle
|
||||
- Nach dem Tutorium für eine genauere Erklärung
|
||||
- Nutzt die Studienleistung zum Üben
|
||||
- Vor der Klausur werde ich wieder ein Zusatz-Tutorium veranstalten um euch Abschließend auf die Klausur vorzubereiten
|
||||
- Gerne auch Fragen übers Studieren/Studium
|
||||
|
||||
---
|
||||
|
||||
# Zusammenfassung der Vorlesung
|
||||
Was müsst ihr Wissen?
|
||||
|
||||
---
|
||||
|
||||
# Was muss ich installieren?
|
||||
|
||||
- [Setup Guide](https://proglang.github.io/teaching/24ws/eidp/setup.html)
|
||||
- Zusammengefasst:
|
||||
- WSL/Linux
|
||||
- Python 3.12.x
|
||||
- VS Code
|
||||
- VS Code-Extensions:
|
||||
- Python, Pylance, Flake8, autopep8
|
||||
- Git
|
||||
|
||||
---
|
||||
|
||||
# Warum Linux statt WSL?
|
||||
|
||||
- Ihr werdet euch im Studium sowieso mit Linux auseinandersetzen
|
||||
- Endlich kein Windows mehr
|
||||
- Wir haben nicht einen Tutor der Windows benutzt
|
||||
- Deutlich kürzere Installation auch in zukünftigen Vorlesungen
|
||||
- Ihr lernt den Umgang mit der Shell deutlich schneller
|
||||
|
||||
---
|
||||
|
||||
# Was ist die Shell?
|
||||
|
||||
- Die Shell ermöglicht wie die UI mit dem Betriebssystem zu interagieren
|
||||
- Ermöglicht einen präziseren Umgang mit eurem Betriebssystem
|
||||
- Ungewohnt am Anfang, aber mit etwas Übung deutlich effektiver/schneller als auf UIs angewiesen zu sein
|
||||
- Viele Prozesse sind nur mit der Shell möglich (haben kein UI)
|
||||
|
||||
---
|
||||
|
||||
# Was ist Git?
|
||||
|
||||
- Git dient der Versionskontrolle
|
||||
- Ihr könnt neue/veränderte Datein zu Git hinzufügen um so eine Version einer Datei abzuspeichern
|
||||
- Hat noch viele weiter Funktionen für die Software Entwicklung
|
||||
- Für die Vorlesung braucht ihr lediglich `add`, `commit`, `pull`, `push`, (`status`)
|
||||
|
||||
---
|
||||
|
||||
<div class='columns'>
|
||||
<div>
|
||||
|
||||
# `git status`
|
||||
|
||||
- Zeigt neue/veränderte/staged Datein an
|
||||
</div>
|
||||
<div>
|
||||
|
||||
# `git add`
|
||||
|
||||
- Fügt neue/veränderte Datein in den `staged` Bereich von Git
|
||||
- Diese Datein sind noch nicht vollständig gespeichert
|
||||
- Mit `git status` könnt ihr sehen welche bereits `staged` sind
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
---
|
||||
|
||||
# `git commit -m "commit message"`
|
||||
|
||||
- "Speichert" die Dateien, welche mit `git add` hinzugefügt wurden
|
||||
|
||||
---
|
||||
|
||||
# Git-Server
|
||||
|
||||
- Die Dateien sind jetzt aber nur lokal gespeichert
|
||||
- Git-Server ermöglichen den Austausch von Quellcode im Internet
|
||||
- So können auch mehrere an einem Quellcode arbeiten
|
||||
|
||||
---
|
||||
|
||||
<div class='columns'>
|
||||
<div>
|
||||
|
||||
# `git pull`
|
||||
|
||||
- Der Git-Server wird nach neuen Änderungen gefragt
|
||||
- Die neuen Änderungen werden heruntergeladen
|
||||
</div>
|
||||
|
||||
<div>
|
||||
|
||||
# `git push`
|
||||
|
||||
- Lokal gespeicherte Änderungen werden auf den Git-Server hochgeladen
|
||||
- Davor muss `git commit` verwendet worden sein
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
---
|
||||
|
||||
<div class=columns>
|
||||
<div>
|
||||
|
||||
# Python
|
||||
|
||||
- Wird in `.py` Datein geschrieben
|
||||
- Wird mit dem Befehl `python filename.py` ausgeführt
|
||||
- Python übersetzt die Datei, und führt diese Zeile für Zeile aus
|
||||
</div>
|
||||
|
||||
<div>
|
||||
|
||||
# Python Shell
|
||||
|
||||
- Interaktive Shell (ähnlich wie die Linux Shell)
|
||||
- wartet auf Benutzereingaben
|
||||
- interpretiert Benutzereingaben nacheinander
|
||||
- Wird mit dem Befehl `python` ausgeführt
|
||||
</div>
|
||||
</div>
|
||||
|
||||
---
|
||||
|
||||
# Zahlentypen
|
||||
|
||||
<div class='columns'>
|
||||
|
||||
<div>
|
||||
|
||||
## `int`
|
||||
|
||||
- Ganzzahlen $\mathbb{Z}$
|
||||
- In Python nur durch verfügbaren Speicher limitiert
|
||||
|
||||
</div>
|
||||
|
||||
<div>
|
||||
|
||||
## `float`
|
||||
|
||||
- Rationale Zahlen $\mathbb{R}$
|
||||
- neben Speicherlimitierung auch noch limitiert in der Genauigkeit (Floatpoint Precision)
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
---
|
||||
|
||||
# Rechenoperation
|
||||
|
||||
- Wie mans kennt eigentlich
|
||||
- Addition `+`
|
||||
- Subtraktion `-`
|
||||
- Multiplikation `*`
|
||||
- Division `/`
|
||||
- Ganzzahlige Division `//`
|
||||
- Potenzen `**`
|
||||
- Modulo `%`
|
||||
|
||||
---
|
||||
|
||||
# Übungsblatt 1 + Installationsprobleme
|
Before Width: | Height: | Size: 96 KiB |
BIN
Tutorium/tut01/tutorium-01.pdf
Normal file
@ -1,140 +0,0 @@
|
||||
|
||||
# Zusammenfassung der Vorlesung
|
||||
|
||||
## Variablen und Zuweisungen
|
||||
|
||||
```python
|
||||
variable = 42
|
||||
print(variable)
|
||||
print(variable + variable)
|
||||
```
|
||||
|
||||
- Case-Sensitive
|
||||
- A-z, 0-9, _
|
||||
- Keine Schlüsselwörte
|
||||
|
||||
### Kurzzuweisung
|
||||
|
||||
Operationen und Zuweisung können in einem Schritt durchgeführt werden mit `${Operation}=`
|
||||
|
||||
```python
|
||||
number = 42
|
||||
number **= 42
|
||||
assert number == 42 ** 42
|
||||
number += 42
|
||||
assert number == 42 ** 42 + 42
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Assert
|
||||
|
||||
- Erwartet einen wahren Ausdruck, wenn dieser nicht erfüllt ist gibt es einen Fehler
|
||||
- Gut um etwas schnell zu testen
|
||||
|
||||
```py
|
||||
number = 42
|
||||
assert number == 42
|
||||
bigger_number = number ** number
|
||||
assert number ** number == bigger_number
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Typen
|
||||
|
||||
```py
|
||||
number = 42
|
||||
string = '42'
|
||||
assert number != string
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Funktionen
|
||||
|
||||
### Standardfunktionen
|
||||
|
||||
- Typen konvertieren
|
||||
|
||||
```python
|
||||
>>> int(2.6)
|
||||
2
|
||||
>>> float(2)
|
||||
2.0
|
||||
>>> str(2.0)
|
||||
'2.0'
|
||||
>>> type(2.0)
|
||||
<class 'float'>
|
||||
```
|
||||
|
||||
- Input/Output
|
||||
|
||||
```python
|
||||
>>> input("Hier Input geben: ")
|
||||
Hier Input geben: 42
|
||||
'42'
|
||||
>>> print(42)
|
||||
42
|
||||
```
|
||||
|
||||
### Funktionen kombinieren
|
||||
|
||||
```python
|
||||
>>> int(str(2))
|
||||
2
|
||||
>>> str(int('2'))
|
||||
'2'
|
||||
```
|
||||
|
||||
### Neue Funktionen definieren
|
||||
|
||||
Mit `def` können neue Funktionen definiert werden
|
||||
|
||||
```python
|
||||
def my_print_func(some_text):
|
||||
print(some_text)
|
||||
|
||||
def my_add(a, b):
|
||||
return a + b
|
||||
|
||||
my_print_func(my_add(1, 2)) # prints 3
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Imports
|
||||
|
||||
Mit `import` können Module (andere Python-Datein) importiert und benutzt werden
|
||||
|
||||
```python
|
||||
import math
|
||||
|
||||
print(math.cos(math.pi)) # prints -1.0
|
||||
```
|
||||
|
||||
Mit `from` und `import` können Sachen aus einem Modul spezifisch importiert werden
|
||||
|
||||
```python
|
||||
from math import cos, pi
|
||||
|
||||
print(cos(pi)) # prints -1.0
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Scopes
|
||||
|
||||
Variabeln existieren in so genannten Scopes, am besten veranschaulicht man sich das einfach:
|
||||
|
||||
```python
|
||||
GLOBAL_X = 42
|
||||
|
||||
def my_func():
|
||||
x_in_func = 42 # x_in_func wird hier im Scope der Funktion erstellt
|
||||
print(GLOBAL_X) # GLOBAL_X ist im globalen Scope,
|
||||
# also auch hier im Funktions-Scope, weil dieser Scope auch im globalen Scope ist
|
||||
|
||||
print(x_in_func) # wirft einen Fehler, weil x_in_func nicht im Scope ist
|
||||
print(GLOBAL_X) # Global Scope
|
||||
```
|
@ -1,57 +0,0 @@
|
||||
# Korrektur Exercise-01
|
||||
|
||||
## Häufige Fehler
|
||||
|
||||
### Aufgabe 1.1
|
||||
|
||||
Wer weiterhin Probleme bei der Installation hat, bitte nach dem Tutorat
|
||||
|
||||
### Aufgabe 1.2
|
||||
|
||||
- Terminierung war nicht erfüllt für $x,y=0$ aber nicht $x = y$
|
||||
- Dadurch auch kein Algorithmus laut Definition der Vorlesung, gab keinen Abzug wenn man alle Bedingungen als erfüllt ansah
|
||||
|
||||
### Aufgabe 1.3
|
||||
|
||||
- Es ging vor allem um den Unterschied zwischen der Python-Shell und dem normalen ausführen von `.py` Dateien
|
||||
|
||||
### Aufgabe 1.4
|
||||
|
||||
- Achtet auf die genau Anforderung!!!
|
||||
|
||||
```sh
|
||||
Python
|
||||
Python
|
||||
Python
|
||||
```
|
||||
|
||||
heißt nicht
|
||||
|
||||
```sh
|
||||
|
||||
Python
|
||||
Python
|
||||
Python
|
||||
```
|
||||
|
||||
oder
|
||||
|
||||
```sh
|
||||
|
||||
Python
|
||||
Python
|
||||
Python
|
||||
```
|
||||
|
||||
oder
|
||||
|
||||
```sh
|
||||
Python
|
||||
Python
|
||||
Python
|
||||
|
||||
```
|
||||
|
||||
## Punkteverteilung
|
||||
|
||||

|
@ -1,5 +0,0 @@
|
||||
# [Exercise-02](https://proglang.informatik.uni-freiburg.de/teaching/info1/2023/exercise/sheet02.pdf)
|
||||
|
||||
- Abgabe 30.10.2023 9:00 Uhr
|
||||
- Achtet auf den Build-Output (Linter, Notes, ...)
|
||||
- Fragen?
|
@ -1,212 +0,0 @@
|
||||
# Beispiel Git-Workflow
|
||||
|
||||
---
|
||||
|
||||
## SSH-Key generieren und im Git hinzufügen
|
||||
|
||||
### Linux & Mac-OS
|
||||
|
||||
Generiere einen Key mit
|
||||
|
||||
```sh
|
||||
ssh-keygen -t ed25519 -C "you@mail.com"
|
||||
```
|
||||
|
||||
Gib den Key in die Konsole aus mit
|
||||
|
||||
```sh
|
||||
cat ~/.ssh/id_ed25519.pub
|
||||
```
|
||||
|
||||
Kopiere mit Strg+Shift+C oder Rechtklick den Key aus dem Terminal und fügen ihn im [Git](https://git.laurel.informatik.uni-freiburg.de/user/settings/keys) als SSH Key hinzu.
|
||||
|
||||
### Windows
|
||||
|
||||
Generiere einen Key und kopiere ihn
|
||||
|
||||
```ps
|
||||
ssh-keygen.exe -t ed25519 -C "you@mail.com"
|
||||
cat ~/.ssh/id_ed25519.pub | clip
|
||||
```
|
||||
|
||||
fügen dann den Key im [Git](https://git.laurel.informatik.uni-freiburg.de/user/settings/keys) als SSH Key hinzu.
|
||||
|
||||
### Mac
|
||||
|
||||
Generiere einen Key mit
|
||||
|
||||
```sh
|
||||
ssh-keygen -t ed25519 -C "you@mail.com"
|
||||
pbcopy < ~/.ssh/id_ed25519.pub
|
||||
```
|
||||
|
||||
fügen dann den Key im [Git](https://git.laurel.informatik.uni-freiburg.de/user/settings/keys) als SSH Key hinzu.
|
||||
|
||||
---
|
||||
|
||||
## Das Repository clonen
|
||||
|
||||
Erstmal ist es wichtig wie man sich im Terminal überhaupt bewegt und umschaut. Wenn wir das Terminal starten, egal ob in Windows/Linux/Mac landen wir im Home-Verzeichnis often bezeichnet als `~`. Um den ersten Schritt zu gehen müssen wir erstmal wissen was wir hier überhaupt haben. Hierfür haben wir das Programm `ls`, welches den Inhalt in einem (ohne Argumente im aktuellen) Verzeichnis auflistet. Eine Beispielausgabe wäre:
|
||||
|
||||
```sh
|
||||
nils@linux ~> ls
|
||||
total 16
|
||||
drwxr-xr-x 2 nils nils 4096 Oct 27 02:14 Desktop/
|
||||
drwxr-xr-x 2 nils nils 4096 Oct 27 02:16 Downloads/
|
||||
drwxr-xr-x 2 nils nils 4096 Oct 27 02:14 Pictures/
|
||||
drwxr-xr-x 2 nils nils 4096 Oct 27 02:14 Videos/
|
||||
```
|
||||
|
||||
Nun können wir uns in die anderen Verzeichnisse bewegen mit `cd` (change directory).
|
||||
|
||||
```sh
|
||||
nils@linux ~> cd Downloads/
|
||||
nils@linux ~/Downloads> ls
|
||||
total 0
|
||||
-rw-r--r-- 1 nils nils 0 Oct 27 02:19 cat.png
|
||||
```
|
||||
|
||||
mit `cd ..` können wir uns jetzt ein Verzeichnis wieder nach oben bewegen
|
||||
|
||||
```sh
|
||||
nils@linux ~/Downloads> cd ..
|
||||
nils@linux ~> ls
|
||||
total 16
|
||||
drwxr-xr-x 2 nils nils 4096 Oct 27 02:14 Desktop/
|
||||
drwxr-xr-x 2 nils nils 4096 Oct 27 02:19 Downloads/
|
||||
drwxr-xr-x 2 nils nils 4096 Oct 27 02:14 Pictures/
|
||||
drwxr-xr-x 2 nils nils 4096 Oct 27 02:14 Videos/
|
||||
```
|
||||
|
||||
nun clonen wir das Repository indem wir ins [Git](https://git.laurel.informatik.uni-freiburg.de/2021WS-EiP/) gehen, auf unser persönliches Repository gehen. Und oben bei **SSH** auf **Copy**/**Kopieren** gehen.
|
||||
|
||||
Nun müssen wir einfach nur noch folgenden Befehl eingeben
|
||||
|
||||
```sh
|
||||
nils@linux ~> git clone ssh://git@git.laurel.informatik.uni-freiburg.de:2222/2021WS-EiP/np163.git
|
||||
Cloning into 'np163'...
|
||||
The authenticity of host '[git.laurel.informatik.uni-freiburg.de]:2222 ([132.230.166.132]:2222)' can't be established.
|
||||
ED25519 key fingerprint is SHA256:zR3d+3MewcoiAuwVidHYfWcsNjT/OVz5FR6IwIyTNCs.
|
||||
This key is not known by any other names
|
||||
Are you sure you want to continue connecting (yes/no/[fingerprint])? yes
|
||||
Warning: Permanently added '[git.laurel.informatik.uni-freiburg.de]:2222' (ED25519) to the list of known hosts.
|
||||
remote: Enumerating objects: 594, done.
|
||||
remote: Counting objects: 100% (594/594), done.
|
||||
remote: Compressing objects: 100% (573/573), done.
|
||||
remote: Total 594 (delta 336), reused 0 (delta 0), pack-reused 0
|
||||
Receiving objects: 100% (594/594), 86.90 KiB | 2.63 MiB/s, done.
|
||||
Resolving deltas: 100% (336/336), done.
|
||||
```
|
||||
|
||||
nun können wir mit `ls` nachschauen dass ein neuer Ordner erschienen ist, in meinem Fall **np163**.
|
||||
|
||||
```sh
|
||||
nils@linux ~> ls
|
||||
total 20
|
||||
drwxr-xr-x 2 nils nils 4096 Oct 27 02:14 Desktop/
|
||||
drwxr-xr-x 2 nils nils 4096 Oct 27 02:19 Downloads/
|
||||
drwxr-xr-x 2 nils nils 4096 Oct 27 02:14 Pictures/
|
||||
drwxr-xr-x 2 nils nils 4096 Oct 27 02:14 Videos/
|
||||
drwxr-xr-x 17 nils nils 4096 Oct 27 02:24 np163/
|
||||
```
|
||||
|
||||
Nun können wir diesen Ordner in VSCode öffnen und haben einen Workspace um die Übungsaufgaben zu bearbeiten.
|
||||
|
||||
---
|
||||
|
||||
Nun bewegen wir uns ins Git-Verzeichnis mit `cd np163`. Und führen unseren ersten Git-Command aus `git status`
|
||||
|
||||
```sh
|
||||
nils@linux ~/np163 (master)> git status
|
||||
On branch master
|
||||
Your branch is up to date with 'origin/master'.
|
||||
|
||||
nothing to commit, working tree clean
|
||||
```
|
||||
|
||||
wir sehen, dass aktuell noch nichts im Verzeichnis geändert wurde. Das ändern wir jetzt indem wir in VSCode eine `hello_world.py` erstellen. Und den `git status` wiederholen
|
||||
|
||||
```sh
|
||||
nils@linux ~/np163 (master)> git status
|
||||
On branch master
|
||||
Your branch is up to date with 'origin/master'.
|
||||
|
||||
Untracked files:
|
||||
(use "git add <file>..." to include in what will be committed)
|
||||
hello_world.py
|
||||
|
||||
nothing added to commit but untracked files present (use "git add" to track)
|
||||
```
|
||||
|
||||
hier schlägt uns Git auch direkt schon vor `git add` zu verwenden um die neue Datei hinzuzufügen.
|
||||
|
||||
```sh
|
||||
nils@linux ~/np163 (master)> git add hello_world.py
|
||||
nils@linux ~/np163 (master)> git status
|
||||
On branch master
|
||||
Your branch is up to date with 'origin/master'.
|
||||
|
||||
Changes to be committed:
|
||||
(use "git restore --staged <file>..." to unstage)
|
||||
new file: hello_world.py
|
||||
```
|
||||
|
||||
mit `git add -A` können alle aktuelle Änderungen hinzugefügt werden.
|
||||
nun können wir die Datei in unser Git eintragen indem wir `git commit -m 'meine nachricht'` verwenden.
|
||||
|
||||
```sh
|
||||
nils@linux ~/np163 (master)> git commit -m 'created hello_world.py'
|
||||
[master 4191d5b] created hello_world.py
|
||||
1 file changed, 0 insertions(+), 0 deletions(-)
|
||||
create mode 100644 hello_world.py
|
||||
```
|
||||
|
||||
und diese Änderung dann mit `git push` hochladen.
|
||||
|
||||
```sh
|
||||
nils@linux ~/np163 (master)> git push
|
||||
Enumerating objects: 4, done.
|
||||
Counting objects: 100% (4/4), done.
|
||||
Delta compression using up to 16 threads
|
||||
Compressing objects: 100% (2/2), done.
|
||||
Writing objects: 100% (3/3), 285 bytes | 285.00 KiB/s, done.
|
||||
Total 3 (delta 1), reused 0 (delta 0), pack-reused 0
|
||||
remote: . Processing 1 references
|
||||
remote: Processed 1 references in total
|
||||
To ssh://git.laurel.informatik.uni-freiburg.de:2222/2021WS-EiP/np163.git
|
||||
06b6eb7..4191d5b master -> master
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Git - VSCode
|
||||
|
||||
Zunächst erstellen wir eine Datei mit dem Namen `hello_world.py` über die Verzeichnisverwaltung von VSCode. Wir sehen dass die neue Datei bereits grün angezeigt, weil es eine neue Datei im Git ist.
|
||||
|
||||

|
||||
|
||||
Nun wechsel wir die Ansicht von der Verzeichnisverwaltung zu Git ganz links außen.
|
||||
|
||||

|
||||
|
||||
Nun drücken wir bei unserer neu erstellten Datei auf das `+`. Diese wird dann als `Staged Changes` angezeigt.
|
||||
|
||||

|
||||
|
||||
Nun legen wir eine Nachricht fest welche die Änderungen representiert und beschreibt.
|
||||
|
||||

|
||||
|
||||
Nun drücken wir auf `Commit` und nun sind unsere Änderungen übernommen.
|
||||
|
||||

|
||||
|
||||
Nun können wir noch auf `Sync Changes` drücken um die Änderungen auch an den Git-Services **Gitea** zu schicken. Danach werden keine weiteren Dateien mehr im Git-Reiter angezeigt und online sehen wir dass unsere Änderungen hochgeladen wurden.
|
||||
|
||||

|
||||
|
||||

|
||||
|
||||
---
|
||||
|
||||
Für Anregung gerne eine kurze [Mail](mailto:nils@narl.io) schreiben.
|
@ -1,21 +0,0 @@
|
||||
# Tutorium 02 - 27.10.2023
|
||||
|
||||
## Today
|
||||
|
||||
- [Korrektur Exercise-01](./CORRECTION.md)
|
||||
- [Zusammenfassung Vorlesung](./COMPREHENSION.md)
|
||||
- [Exercise-02](./EXERCISE-02.md)
|
||||
- [Beispiel Git-Workflow](./GIT.md)
|
||||
|
||||
## About me
|
||||
|
||||
- [nils@narl.io](mailto:nils@narl.io)
|
||||
- Discord: [.narl](https://discord.com/users/208979474988007425)
|
||||
- Telegram: [@narl_np](https://t.me/narl_np)
|
||||
- [Feedback](https://s.narl.io/s/Feedback-Tutorium-01)
|
||||
|
||||
<img src="../../src/img/mailto.png" height="200">
|
||||
<img src="../../src/img/discord.png" height="200">
|
||||
<img src="../../src/img/telegram.png" height="200">
|
||||
<img src="../../src/img/feedback-google-forms.png" height="200">
|
||||
<img src="./src/tutorium-02.png" height="200">
|
Before Width: | Height: | Size: 17 KiB |
@ -1 +0,0 @@
|
||||
print("hello world")
|
Before Width: | Height: | Size: 87 KiB |
Before Width: | Height: | Size: 118 KiB |
Before Width: | Height: | Size: 18 KiB |
Before Width: | Height: | Size: 22 KiB |
Before Width: | Height: | Size: 21 KiB |
Before Width: | Height: | Size: 14 KiB |
Before Width: | Height: | Size: 13 KiB |
Before Width: | Height: | Size: 8.5 KiB |
@ -1,65 +0,0 @@
|
||||
# Tutorium 04 - 10.11.2023
|
||||
|
||||
## [Exercise 02](https://proglang.informatik.uni-freiburg.de/teaching/info1/2023/exercise/sheet02.pdf) und [Exercise 03](https://proglang.informatik.uni-freiburg.de/teaching/info1/2023/exercise/sheet03.pdf)
|
||||
|
||||
### Punkteverteilung Exercise 02
|
||||
|
||||

|
||||
|
||||
### Punkteverteilung Exercise 03
|
||||
|
||||

|
||||
|
||||
### Häufige Fehler
|
||||
|
||||
- **Schaut genau was muss ausgegeben werden!!!**
|
||||
- Achtet auf den Build-Output
|
||||
- **Linter-Error?** (-0.5 Punkte pro Datei)
|
||||
- **Syntax-Error?** (0 Punkte ab Exercise 4)
|
||||
- **Stunden eingetragen?** (-0.5 Punkte)
|
||||
- lest euch die Aufgaben genau durch
|
||||
- kommentiert keinen Quellcode aus, lasst ihn weg, oder lasst ihn stehen
|
||||
- Testet euren Code mit `assert`
|
||||
- später lernen wir noch bessere Tests kennen
|
||||
- lasst eure `assert` nicht einfach in der Logik stehen!
|
||||
|
||||
```py
|
||||
def some_function(arg):
|
||||
assert arg <= 360 # WRONG!
|
||||
return calculate(arg)
|
||||
```
|
||||
|
||||
```py
|
||||
def some_function(arg) -> float:
|
||||
return calculate(arg)
|
||||
|
||||
if __name__ == "__main__":
|
||||
# Right! Nur testen ob alles tut, mehr nicht
|
||||
# und in __main__ packen, damit nicht jeder import die asserts aufruft
|
||||
assert some_function(0.69) <= 42
|
||||
assert some_function(0.420) <= 1337
|
||||
```
|
||||
|
||||
## Vorstellen/Vorrechnen
|
||||
|
||||
- mz242
|
||||
- vb205
|
||||
|
||||
## [Exercise 04](https://proglang.informatik.uni-freiburg.de/teaching/info1/2023/exercise/sheet04.pdf)
|
||||
|
||||
- Abgabe Montag 9:00 im [Git](https://git.laurel.informatik.uni-freiburg.de/)
|
||||
- Typannotationen jetzt wichtig.
|
||||
- `min` Funktion darf in 4.1 nicht benutzt werden.
|
||||
- Aufgabenteile werden mit **0 Punkten bewertet**, wenn:
|
||||
- Dateien und Funktionen nicht so benannt sind, wie im Aufgabentext gefordert;
|
||||
- Dateien falsche Formate haben, z.B. PDF statt plaintext;
|
||||
- Pythonskripte wegen eines Syntaxfehlers nicht ausführbar sind.
|
||||
- Verwenden Sie nur Befehle und Programmiertechniken, die Inhalt der bisherigen Vorlesungen (bis zum Abgabetermin) und Übungsblättern waren. (Ausnahme: f-Strings)
|
||||
- Fragen?
|
||||
|
||||
|
||||
# Nachkorrektur
|
||||
|
||||
- mw793
|
||||
- fk439 (Zeitangabe)
|
||||
- jb1484 (Verspätete Akinator)
|
Before Width: | Height: | Size: 18 KiB |
Before Width: | Height: | Size: 17 KiB |
@ -1,125 +0,0 @@
|
||||
# Tutorium 05 - 17.11.2023
|
||||
|
||||
## Korrektur [Exercise-04](https://proglang.informatik.uni-freiburg.de/teaching/info1/2023/exercise/sheet04.pdf)
|
||||
|
||||
### Punkteverteilung
|
||||
|
||||

|
||||
|
||||
### Häufige Fehler
|
||||
|
||||
- Type Annotations
|
||||
- Print-Statements, Top-Level Statements in Logik/nicht in
|
||||
|
||||
```python
|
||||
if __name__ == "__main__":
|
||||
assert # some test
|
||||
```
|
||||
|
||||
- Ich kann euch prinzipiell immer 0 Punkte geben wenn Ihr etwas verwendet, was nicht Teil der Vorlesung war
|
||||
- Lest die Aufgabenstellungen/Hinweise auf dem Blatt
|
||||
- Benennt eure Dateien/Methoden richtig
|
||||
|
||||
## Vorrechnen
|
||||
|
||||
1. `lists.py`
|
||||
- a) `even`: no43
|
||||
- b) `min`: cl393
|
||||
- c) `max`: mt367
|
||||
2. `euler.py`
|
||||
- a) `fac`: au56
|
||||
- b) `approx_e`: rw208
|
||||
3. `binary.py`
|
||||
- a) `to_num`: ua28
|
||||
- b) `stream_to_nums`: md489
|
||||
|
||||
## [Exercise-05](https://proglang.informatik.uni-freiburg.de/teaching/info1/2023/exercise/sheet05.pdf)
|
||||
|
||||
- Abgabe Montag 09:00 Uhr im [git](https://git.laurel.informatik.uni-freiburg.de/)
|
||||
- Probleme beim installieren von `pygame`?
|
||||
|
||||
## Übungsaufgaben
|
||||
|
||||
### [Primes](./src/primes.py)
|
||||
|
||||
Schreibe eine Funktion `prime_factorization` die eine Ganzzahl `n` entgegen nimmt und alle Primfaktoren berrechnet und die gegebene Zahl `n` in einen Paar mit den Primfaktoren als Liste zurückgibt. Denkt dabei an die richtigen Type Annotations
|
||||
|
||||
```python
|
||||
def prime_factorization(n):
|
||||
pass
|
||||
```
|
||||
|
||||
### [Dataclass](./src/data_classes.py)
|
||||
|
||||
Schreiben Sie eine Datenklasse `Fraction` (Bruch), beachten Sie dabei die Type Annotations. Ein Bruch besteht aus einem `divident` und einem `divisor`.
|
||||
|
||||
```python
|
||||
from dataclasses import dataclass
|
||||
|
||||
|
||||
@dataclass
|
||||
class Fraction:
|
||||
pass
|
||||
```
|
||||
|
||||
Nun modellieren wir Hilfsmethoden für unsere Datenklassen, die uns später bei der Logik von Brüchen helfen
|
||||
|
||||
```python
|
||||
# the greatest common divisor of two numbers `a`, `b`
|
||||
def gcd(a, b):
|
||||
pass
|
||||
|
||||
# this shortens a fraction to its most reduced representation
|
||||
def shorten_fraction(fraction):
|
||||
pass
|
||||
```
|
||||
|
||||
Abschließend modellieren wir nun auch noch das Verhalten von Brüchen indem wir Methoden direkt in der Datenklasse erstellen. Type Annotations!
|
||||
|
||||
```python
|
||||
# Multiplication of two fractions
|
||||
# `Fraction(1 / 2) * Fraction(2 / 6) -> Fraction(1, 6)`
|
||||
# Extra: make it possible to multiply `int` with a fraction
|
||||
# `Fraction(1 / 2) * 2 -> Fraction(1 / 4)`
|
||||
def __mul__(self, o):
|
||||
pass
|
||||
|
||||
# The division of two fraction
|
||||
# `Fraction(1 / 2) / Fraction(2 / 6) -> Fraction(3, 2)`
|
||||
# Extra: make it possible to divide `int` with a fraction
|
||||
# `Fraction(1 / 4) / 2 -> Fraction(1 / 2)`
|
||||
def __truediv__(self, o):
|
||||
pass
|
||||
|
||||
# The negative of a fraction
|
||||
# `-Fraction(1 / 2) -> Fraction(-1 / 2)`
|
||||
def __neg__(self):
|
||||
pass
|
||||
|
||||
# The addition of two fractions
|
||||
# `Fraction(1 / 4) + Fraction(2 / 8) -> Fraction(1 / 2)`
|
||||
# Extra: make it possible to add `int` with a fraction
|
||||
# `Fraction(1 / 4) + 1 -> Fraction(5 / 4)`
|
||||
def __add__(self, o):
|
||||
pass
|
||||
|
||||
# The subtraction of two fractions
|
||||
# `Fraction(1 / 2) - Fraction(1 / 4) -> Fraction(1 / 4)`
|
||||
# Extra: make it possible to subtract `int` with a fraction
|
||||
# `Fraction(5 / 2) - 1 -> Fraction(3 / 2)`
|
||||
def __sub__(self, o):
|
||||
pass
|
||||
|
||||
# The `equal`-function is == and should only care about reduced fractions
|
||||
# `Fraction(1 / 2) == Fraction(2 / 4)` is True
|
||||
def __eq__(self, o):
|
||||
pass
|
||||
|
||||
# The `not equal`-function is != and should only care about reduced fractions exactly as equal
|
||||
def __neq__(self, o):
|
||||
pass
|
||||
|
||||
# The str function should return this string `(divident / divisor)`
|
||||
def __str__(self):
|
||||
pass
|
||||
```
|
Before Width: | Height: | Size: 22 KiB |
@ -1,88 +0,0 @@
|
||||
from dataclasses import dataclass
|
||||
|
||||
|
||||
def gcd(a: int, b: int) -> int:
|
||||
x = abs(a)
|
||||
y = abs(b)
|
||||
while (y):
|
||||
x, y = y, x % y
|
||||
return x
|
||||
|
||||
|
||||
def shorten_fraction(fraction: 'Fraction') -> 'Fraction':
|
||||
g: int = gcd(fraction.divident, fraction.divisor)
|
||||
return Fraction(fraction.divident // g, fraction.divisor // g)
|
||||
|
||||
|
||||
@dataclass
|
||||
class Fraction:
|
||||
divident: int
|
||||
divisor: int
|
||||
|
||||
def __neg__(self: 'Fraction') -> 'Fraction':
|
||||
return -1 * self
|
||||
|
||||
def __mul__(self: 'Fraction', o: 'Fraction | int') -> 'Fraction':
|
||||
if isinstance(o, int):
|
||||
o = Fraction(o, 1)
|
||||
return shorten_fraction(Fraction(self.divident * o.divident,
|
||||
self.divisor * self.divisor))
|
||||
|
||||
def __rmul__(self: 'Fraction', o: 'Fraction | int') -> 'Fraction':
|
||||
return self * o
|
||||
|
||||
def __truediv__(self: 'Fraction', o: 'Fraction | int') -> 'Fraction':
|
||||
if isinstance(o, int):
|
||||
o = Fraction(o, 1)
|
||||
return shorten_fraction(Fraction(self.divident * o.divisor,
|
||||
self.divisor * o.divident))
|
||||
|
||||
def __rtruediv___(self: 'Fraction', o: 'Fraction | int') -> 'Fraction':
|
||||
return self / o
|
||||
|
||||
def __add__(self: 'Fraction', o: 'Fraction | int') -> 'Fraction':
|
||||
if isinstance(o, int):
|
||||
o = Fraction(o, 1)
|
||||
g: int = gcd(self.divisor, o.divisor)
|
||||
l: int = abs(self.divisor * o.divisor) // g
|
||||
return shorten_fraction(Fraction(self.divident
|
||||
* (l // self.divisor)
|
||||
+ o.divident
|
||||
* (l // o.divisor), l))
|
||||
|
||||
def __radd__(self: 'Fraction', o: 'Fraction | int') -> 'Fraction':
|
||||
return self + o
|
||||
|
||||
def __sub__(self: 'Fraction', o: 'Fraction | int') -> 'Fraction':
|
||||
if isinstance(o, int):
|
||||
o = Fraction(o, 1)
|
||||
return self + -o
|
||||
|
||||
def __rsub__(self: 'Fraction', o: 'Fraction | int') -> 'Fraction':
|
||||
return self - o
|
||||
|
||||
def __eq__(self: 'Fraction', o: 'Fraction | int') -> bool:
|
||||
if isinstance(o, int):
|
||||
o = Fraction(o, 1)
|
||||
shorten_self: 'Fraction' = shorten_fraction(self)
|
||||
shorten_o: 'Fraction' = shorten_fraction(o)
|
||||
return (shorten_self.divident == shorten_o.divident
|
||||
and shorten_self.divisor == shorten_o.divisor)
|
||||
|
||||
def __neq__(self: 'Fraction', o: 'Fraction | int') -> bool:
|
||||
return not (self == o)
|
||||
|
||||
def __str__(self: 'Fraction'):
|
||||
return f"({self.divident} / {self.divisor})"
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
assert Fraction(1, 1) == 1
|
||||
assert Fraction(1, 2) == (out := Fraction(2, 4) /
|
||||
Fraction(g := gcd(2, 4), g)), f"!= {out}"
|
||||
assert (sol := Fraction(9, 20)) == (
|
||||
res := Fraction(1, 5) + Fraction(1, 4)), f"!= {out}"
|
||||
assert (sol := Fraction(-9, 20)) == (
|
||||
res := Fraction(1, -5) + Fraction(-1, 4)), f"!= {out}"
|
||||
assert (sol := Fraction(-1, 20)) == (
|
||||
res := Fraction(1, 5) + Fraction(-1, 4)), f"!= {out}"
|
@ -1,34 +0,0 @@
|
||||
def prime_factorization(num: int) -> tuple[int, list[int]]:
|
||||
# our list of primefactors
|
||||
primefactors: list[int] = []
|
||||
# our first prime number
|
||||
prime: int = 2
|
||||
# we don't want to modify our number and copy it to `n`
|
||||
n: int = num
|
||||
|
||||
# iterate until we find the last step the square of
|
||||
# our prime is bigger than our rest number
|
||||
while n != 1:
|
||||
# if our number is dividable by our prime
|
||||
if n % prime == 0:
|
||||
# we can add the prime to our primefactors
|
||||
primefactors.append(prime)
|
||||
# and divide our rest number by the prime number
|
||||
n //= prime
|
||||
else:
|
||||
# increment until next prime number
|
||||
prime += 1
|
||||
|
||||
# finally return our tuple with our number and primefactors
|
||||
return (num, primefactors)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
assert (sol := (100, [2, 2, 5, 5])) == (
|
||||
res := prime_factorization(100)), f"{res} is not {sol}"
|
||||
assert (sol := (69, [3, 23])) == (
|
||||
res := prime_factorization(69)), f"{res} is not {sol}"
|
||||
assert (sol := (31, [31])) == (
|
||||
res := prime_factorization(31)), f"{res} is not {sol}"
|
||||
assert (sol := (123490823022, [2, 3, 3, 3, 3, 7, 7, 7, 1123, 1979])) == (
|
||||
res := prime_factorization(123490823022)), f"{res} is not {sol}"
|
@ -1,68 +0,0 @@
|
||||
from dataclasses import dataclass
|
||||
|
||||
|
||||
# the greatest common divisor of two numbers `a`, `b`
|
||||
def gcd(a, b):
|
||||
pass
|
||||
|
||||
|
||||
# this shortens a fraction to its most reduced representation
|
||||
def shorten_fraction(fraction):
|
||||
pass
|
||||
|
||||
|
||||
@dataclass
|
||||
class Fraction:
|
||||
|
||||
# Multiplication of two fractions
|
||||
# `Fraction(1 / 2) * Fraction(2 / 6) -> Fraction(1, 6)`
|
||||
# Extra: make it possible to multiply `int` with a fraction
|
||||
# `Fraction(1 / 2) * 2 -> Fraction(1 / 4)`
|
||||
def __mul__(self, o):
|
||||
pass
|
||||
|
||||
# The division of two fraction
|
||||
# `Fraction(1 / 2) / Fraction(2 / 6) -> Fraction(3, 2)`
|
||||
# Extra: make it possible to divide `int` with a fraction
|
||||
# `Fraction(1 / 4) / 2 -> Fraction(1 / 2)`
|
||||
|
||||
def __truediv__(self, o):
|
||||
pass
|
||||
|
||||
# The negative of a fraction
|
||||
# `-Fraction(1 / 2) -> Fraction(-1 / 2)`
|
||||
|
||||
def __neg__(self):
|
||||
pass
|
||||
|
||||
# The addition of two fractions
|
||||
# `Fraction(1 / 4) + Fraction(2 / 8) -> Fraction(1 / 2)`
|
||||
# Extra: make it possible to add `int` with a fraction
|
||||
# `Fraction(1 / 4) + 1 -> Fraction(5 / 4)`
|
||||
|
||||
def __add__(self, o):
|
||||
pass
|
||||
|
||||
# The subtraction of two fractions
|
||||
# `Fraction(1 / 2) - Fraction(1 / 4) -> Fraction(1 / 4)`
|
||||
# Extra: make it possible to subtract `int` with a fraction
|
||||
# `Fraction(5 / 2) - 1 -> Fraction(3 / 2)`
|
||||
|
||||
def __sub__(self, o):
|
||||
pass
|
||||
|
||||
# The `equal`-function is == and should only care about reduced fractions
|
||||
# `Fraction(1 / 2) == Fraction(2 / 4)` is True
|
||||
|
||||
def __eq__(self, o):
|
||||
pass
|
||||
|
||||
# The `not equal`-function is != and should only care about reduced fractions exactly as equal
|
||||
|
||||
def __neq__(self, o):
|
||||
pass
|
||||
|
||||
# The str function should return this string `(divident / divisor)`
|
||||
|
||||
def __str__(self):
|
||||
pass
|
@ -1,285 +0,0 @@
|
||||
# Tutorium 06 - 24.11.2023
|
||||
|
||||
## Vorab Informationen
|
||||
|
||||
- Kollektiver [Discord](https://s.narl.io/s/discord-invite) mit Tutorium 05 (Daniel Mironow)
|
||||
- Dani's-Tutorium: Mi 16:00 - 18:00, Geb. 106, SR 00 007
|
||||
- Im Discord könnt ihr euch direkt mit uns Tutoren austauschen oder untereinander
|
||||
- Invite: https://s.narl.io/s/discord-invite
|
||||
- Es gibt wieder einen
|
||||
<details>
|
||||
<summary>QR-Code:</summary>
|
||||
<img src="../../src/img/discord-invite.png" height=800>
|
||||
</details>
|
||||
|
||||
## Korrektur Blatt 05
|
||||
|
||||
- am Samstag, ich hab mich etwas vertan bei der Korrektur
|
||||
- Punkteverteilung und häufige Fehler werden hier hinzugefügt
|
||||
|
||||

|
||||
|
||||
### Häufige Fehler
|
||||
|
||||
- Type annotation
|
||||
- `@dataclass` nicht benutzt
|
||||
- mutieren von erstellten Objekt
|
||||
- Call über Class und nicht über Objekt
|
||||
|
||||
## Vorrechnen
|
||||
|
||||
1. Python-Game
|
||||
1. `Vec2`: aw616
|
||||
2. `add_vecs`: fk439
|
||||
3. `Item`: ln200
|
||||
4. `Snake`: lp321
|
||||
5. `Game`: rl173
|
||||
6. `turn_direction`: ih205
|
||||
7. `grow_positions`:
|
||||
8. `collision`:
|
||||
9. `generate_item`:
|
||||
10. `pick_item`:
|
||||
|
||||
## Recap - Was ist neu?
|
||||
|
||||
### Union-Type und Type Definitionen
|
||||
|
||||
- neues `type` Keyword
|
||||
- mit `|` lassen sich Union-Types definieren
|
||||
|
||||
```py
|
||||
type Number = int | float | complex
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Generics (Typvariabeln)
|
||||
|
||||
Manchmal weiß man nicht welcher Typ genau gemeint ist, möchte aber trotzdem "sicherstellen" dass es sich nicht um zwei unterschiedliche handelt:
|
||||
|
||||
```py
|
||||
def some_func[T](some_list: list[T]) -> T:
|
||||
# ...
|
||||
```
|
||||
kleines Beispiel von "Bounds" aus Rust:
|
||||
|
||||
```rust
|
||||
fn some_func<T: Add>(some_list: Vec<T>) -> T {
|
||||
// ...
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
oder noch schöner
|
||||
|
||||
```rust
|
||||
fn some_func<T>(some_list: Vec<T>) -> T
|
||||
where T: Add<Output = T> + Default,
|
||||
{
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
```py
|
||||
@dataclass
|
||||
class Stack[T]:
|
||||
internal_list: list[T]
|
||||
|
||||
def push(self, item: T) -> None:
|
||||
self.internal_list.append(item)
|
||||
|
||||
def pop(self) -> T | None:
|
||||
if len(self.internal_list) == 0:
|
||||
return None
|
||||
return self.internal_list.pop()
|
||||
```
|
||||
|
||||
```py
|
||||
type Optional[T] = T | None
|
||||
```
|
||||
Python ist nicht statisch typisiert (statically-typed)! Bedeutet trotz annotation könnt ihr machen was ihr wollt:
|
||||
|
||||
```py
|
||||
def add(x: int, y: int) -> int:
|
||||
return x + y
|
||||
|
||||
add(2, 2.0) # 4.0
|
||||
```
|
||||
|
||||
genauso ist es bei Generics:
|
||||
|
||||
```py
|
||||
from mylist import MyList
|
||||
|
||||
lst: MyList[int] = MyList()
|
||||
lst.push_back(0)
|
||||
lst.push_back(1)
|
||||
print(lst) # [0, 1]
|
||||
lst.push_back("haha not a number")
|
||||
print(lst) # [0, 1, haha not a number]
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### self
|
||||
|
||||
- mit `self` ruft man das Objekt wessen Verhalten man modelliert
|
||||
- damit kann das Objekt verändert (mutiert) werden
|
||||
- einfache Datenklassen bekommen Objekte die ein Verhalten modellieren
|
||||
- jede Methode eines Objekt bekommt `self` als ersten Parameter und gibt vor wie sich ein Objekt verhält
|
||||
|
||||
```python
|
||||
@dataclass
|
||||
class MyNumber[T]():
|
||||
number: T
|
||||
|
||||
def add(self, value: T) -> T:
|
||||
self.number += value
|
||||
return self.number
|
||||
|
||||
num = MyNumber(3)
|
||||
print(num.add(5)) # 8
|
||||
print(num.number) # 8
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Pattern-Matching
|
||||
|
||||
Mit dem `match` Keyword lassen sich verschiedene Bedingungen *matchen*
|
||||
|
||||
- Zunächst Types:
|
||||
- Wir erstellen die Datenklassen
|
||||
|
||||
```python
|
||||
@dataclass
|
||||
class Point1D[T]:
|
||||
x: T
|
||||
|
||||
|
||||
@dataclass
|
||||
class Point2D[T]:
|
||||
x: T
|
||||
y: T
|
||||
|
||||
|
||||
@dataclass
|
||||
class Point3D[T]:
|
||||
x: T
|
||||
y: T
|
||||
z: T
|
||||
```
|
||||
- Wir erstellen einen Typ Alias `Point`
|
||||
```py
|
||||
type Point[T] = Point1D[T] | Point2D[T] | Point3D[T]
|
||||
```
|
||||
- Nun können wir den Type Alias mit Pattern-Matching auf den eigentlichen Datentypen reduzieren
|
||||
```py
|
||||
def print_point[T](pt: Point[T]) -> None:
|
||||
match pt:
|
||||
case Point1D(x):
|
||||
print(f"Point1D: ({x})")
|
||||
case Point2D(x, y):
|
||||
print(f"Point2D: ({x}, {y})")
|
||||
case Point3D(x, y, z):
|
||||
print(f"Point3D: ({x}, {y}, {z})")
|
||||
case _:
|
||||
print("Not a point!")
|
||||
```
|
||||
- Aber auch Bedingungen wie Werte
|
||||
- Nun erweitern wir unsere `print_point` um Nullpunkte auszugeben
|
||||
```py
|
||||
match pt:
|
||||
case Point1D(0) | Point2D(0, 0) | Point3D(0, 0, 0):
|
||||
print("Nullpunkt!")
|
||||
case Point1D(x):
|
||||
print(f"Point1D: ({x})")
|
||||
case Point2D(x, y):
|
||||
print(f"Point2D: ({x}, {y})")
|
||||
case Point3D(x, y, z):
|
||||
print(f"Point3D: ({x}, {y}, {z})")
|
||||
case _:
|
||||
print("Not a point!")
|
||||
```
|
||||
- Achtung: Reihenfolge der Cases ist wichtig!
|
||||
```py
|
||||
match pt:
|
||||
case Point1D(x):
|
||||
print(f"Point1D: ({x})")
|
||||
case Point2D(x, y):
|
||||
print(f"Point2D: ({x}, {y})")
|
||||
case Point3D(x, y, z):
|
||||
print(f"Point3D: ({x}, {y}, {z})")
|
||||
case Point1D(0) | Point2D(0, 0) | Point3D(0, 0, 0):
|
||||
print("Nullpunkt!")
|
||||
case _:
|
||||
print("Not a point!")
|
||||
```
|
||||
- Guards
|
||||
|
||||
```py
|
||||
match pt:
|
||||
case Point1D(x) if x == 0:
|
||||
print("1-D Nullpunkt!")
|
||||
case Point1D(x):
|
||||
print(f"Point1D: ({x})")
|
||||
case Point2D(x, y):
|
||||
print(f"Point2D: ({x}, {y})")
|
||||
case Point3D(x, y, z):
|
||||
print(f"Point3D: ({x}, {y}, {z})")
|
||||
case _:
|
||||
print("Not a point!")
|
||||
```
|
||||
- Noch mehr Types *matchen*!
|
||||
```py
|
||||
match pt:
|
||||
case Point1D(int):
|
||||
print(f"Ganzzahliger Punkt! {pt.x}")
|
||||
case Point1D(float):
|
||||
print(f"Gleitkomma Punkt! {pt.x}")
|
||||
# ...
|
||||
```
|
||||
- Und es wird immer seltsamer
|
||||
|
||||
```py
|
||||
match some_list:
|
||||
case ["🤡", *other]:
|
||||
print(f"your list starts with 🤡 and the rest is {other}")
|
||||
```
|
||||
## Blatt 06
|
||||
|
||||
- Fragen?
|
||||
|
||||
## Aufgabe: eigene Liste implementieren
|
||||
|
||||
Implementiere eine generische Liste mit `append`, `get` und `remove`, ohne buildin Listen zu verwenden!
|
||||
|
||||
### Konzept einer (einfach verketteten) Liste
|
||||
|
||||
- Es gibt einen Listeneintrag `Element`, der den eigentlichen Wert des Eintrags `value` beinhaltet und einen Verweis auf das nächste Element `next` in der Liste
|
||||
- Um dann einen Eintrag `x` zu finden muss man nur `x`-mal die Liste ablaufen und den Wert auslesen
|
||||
- Wenn man ein Element hinzufügen will muss man lediglich ans Ende der Liste laufen und ein neuen Eintrag erstellen
|
||||
- Wenn man ein Element entfernen will muss man lediglich das nächste Element vom vorherigen auf das nächste Element vom zu entfernenden setzen
|
||||
|
||||
### Hilfestellung
|
||||
|
||||
```py
|
||||
@dataclass
|
||||
class Element[T]:
|
||||
value: T
|
||||
next: 'Element[T] | None'
|
||||
|
||||
class MyList[T]:
|
||||
head: Element[T] | None
|
||||
length: int
|
||||
|
||||
def append(self, value: T) -> None:
|
||||
pass
|
||||
|
||||
def get(self, index: int) -> T | None:
|
||||
pass
|
||||
|
||||
def remove(self, index: int) -> T | None:
|
||||
pass
|
||||
```
|
||||
|
Before Width: | Height: | Size: 19 KiB |
@ -1,131 +0,0 @@
|
||||
from dataclasses import dataclass
|
||||
|
||||
|
||||
@dataclass
|
||||
class Element[T]:
|
||||
value: T
|
||||
next: 'Element[T] | None'
|
||||
|
||||
|
||||
@dataclass
|
||||
class MyList[T]:
|
||||
head: Element[T] | None = None
|
||||
last: Element[T] | None = None
|
||||
length: int = 0
|
||||
|
||||
def push_back(self, value: T):
|
||||
if not self.head:
|
||||
self.head = Element(value, None)
|
||||
self.last = self.head
|
||||
self.length += 1
|
||||
return
|
||||
|
||||
self.last.next = Element(value, None)
|
||||
self.last = self.last.next
|
||||
self.length += 1
|
||||
|
||||
def remove(self, index: int) -> T | None:
|
||||
if self.length <= index:
|
||||
return
|
||||
|
||||
if index == 0:
|
||||
self.head = self.head.next
|
||||
self.last = self.head
|
||||
self.length -= 1
|
||||
|
||||
i = index
|
||||
previous = self.head
|
||||
current = self.head
|
||||
while current and i > 0:
|
||||
previous = current
|
||||
current = current.next
|
||||
i -= 1
|
||||
|
||||
if i != 0 or not current:
|
||||
return
|
||||
if not current.next:
|
||||
self.last = previous
|
||||
previous.next = current.next
|
||||
self.length -= 1
|
||||
return current.value
|
||||
|
||||
def __str__(self) -> str:
|
||||
output = ""
|
||||
current = self.head
|
||||
while current:
|
||||
output += str(current.value)
|
||||
if current.next:
|
||||
output += ", "
|
||||
current = current.next
|
||||
return f"[{output}]"
|
||||
|
||||
def __getitem__(self, index: int) -> T | None:
|
||||
if self.length <= index:
|
||||
return
|
||||
|
||||
current = self.head
|
||||
i = index
|
||||
|
||||
while current and i > 0:
|
||||
current = current.next
|
||||
i -= 1
|
||||
|
||||
if i == 0 and current:
|
||||
return current.value
|
||||
|
||||
def __setitem__(self, index: int, new_value: T):
|
||||
if self.length <= index:
|
||||
return
|
||||
|
||||
current = self.head
|
||||
i = index
|
||||
|
||||
while current and i > 0:
|
||||
current = current.next
|
||||
i -= 1
|
||||
|
||||
if i == 0 and current:
|
||||
current.value = new_value
|
||||
|
||||
def __len__(self) -> int:
|
||||
return self.length
|
||||
|
||||
def __iter__(self) -> 'MyList.Iter[T]':
|
||||
return MyList.Iter(self.head)
|
||||
|
||||
@dataclass
|
||||
class Iter[E]:
|
||||
current: Element[E]
|
||||
|
||||
def __next__(self) -> E:
|
||||
if not self.current:
|
||||
raise StopIteration
|
||||
val = self.current.value
|
||||
self.current = self.current.next
|
||||
return val
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
lst: MyList[int] = MyList()
|
||||
lst.push_back(0)
|
||||
lst.push_back(3)
|
||||
lst.push_back(2)
|
||||
assert lst[0] == 0 and lst[1] == 3 and lst[2] == 2
|
||||
print(lst)
|
||||
lst.remove(1)
|
||||
assert lst[0] == 0 and lst[1] == 2
|
||||
print(lst)
|
||||
lst[1] = 3
|
||||
assert lst[0] == 0 and lst[1] == 3 and len(lst) == 2
|
||||
print(lst)
|
||||
assert lst.remove(1)
|
||||
assert lst.last.value == 0
|
||||
lst.remove(0)
|
||||
assert len(lst) == 0
|
||||
assert not lst.head
|
||||
assert not lst.last
|
||||
|
||||
for i in range(0, 10):
|
||||
lst.push_back(i)
|
||||
for it in lst:
|
||||
print(it)
|
@ -1,20 +0,0 @@
|
||||
from union_types import FilteredList, is_even
|
||||
|
||||
|
||||
class PrintableInt(int):
|
||||
def print(self):
|
||||
print(self)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
lst: FilteredList[PrintableInt] = FilteredList(filter=is_even)
|
||||
lst.append(PrintableInt(0))
|
||||
lst.get(0).print()
|
||||
match lst.get(1):
|
||||
case None:
|
||||
pass
|
||||
case PrintableInt(n):
|
||||
n.print()
|
||||
|
||||
lst.append(PrintableInt(2))
|
||||
lst.get(1).print()
|
@ -1,39 +0,0 @@
|
||||
from dataclasses import dataclass
|
||||
from typing import Callable
|
||||
|
||||
type Optional[T] = T | None
|
||||
|
||||
|
||||
@dataclass
|
||||
class FilteredList[E]:
|
||||
lst: list[E]
|
||||
filter: Callable[[E], bool]
|
||||
|
||||
def __init__(self, filter=lambda _: True):
|
||||
self.lst = []
|
||||
self.filter = filter
|
||||
|
||||
def append(self, item: E):
|
||||
if self.filter(item):
|
||||
self.lst += [item]
|
||||
|
||||
def get(self, index: int) -> Optional[E]:
|
||||
if index < len(self.lst):
|
||||
return self.lst[index]
|
||||
|
||||
def __str__(self) -> str:
|
||||
return str(self.lst)
|
||||
|
||||
|
||||
def is_even(n: int) -> bool:
|
||||
return n % 2 == 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
filter_list = FilteredList(filter=is_even)
|
||||
filter_list.append(0)
|
||||
print(filter_list)
|
||||
filter_list.append(2)
|
||||
print(filter_list)
|
||||
filter_list.append(3)
|
||||
print(filter_list)
|
@ -1,53 +0,0 @@
|
||||
from dataclasses import dataclass
|
||||
|
||||
|
||||
@dataclass
|
||||
class Point1D[T]:
|
||||
x: T
|
||||
|
||||
|
||||
@dataclass
|
||||
class Point2D[T]:
|
||||
x: T
|
||||
y: T
|
||||
|
||||
|
||||
@dataclass
|
||||
class Point3D[T]:
|
||||
x: T
|
||||
y: T
|
||||
z: T
|
||||
|
||||
|
||||
type Point[T] = Point1D[T] | Point2D[T] | Point3D[T]
|
||||
|
||||
|
||||
def print_point[T](pt: Point[T]) -> None:
|
||||
match pt:
|
||||
case Point1D(0) | Point2D(0, 0) | Point3D(0, 0, 0):
|
||||
print("Nullpunkt!")
|
||||
case Point1D(x):
|
||||
print(f"Point1D: ({x})")
|
||||
case Point2D(x, y):
|
||||
print(f"Point2D: ({x}, {y})")
|
||||
case Point3D(x, y, z):
|
||||
print(f"Point3D: ({x}, {y}, {z})")
|
||||
case _:
|
||||
print("Not a point!")
|
||||
|
||||
|
||||
def match_list(some_list: list[str]) -> None:
|
||||
match some_list:
|
||||
case ["🤡", *other]:
|
||||
print(f"your list starts with 🤡 and the rest is {other}")
|
||||
case _:
|
||||
print("your list doesn't start with 🤡")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
print_point(Point1D(1)) # (1)
|
||||
print_point(Point2D(1, 2)) # (1, 2)
|
||||
print_point(Point3D(1, 2, 3)) # (1, 2, 3)
|
||||
print_point(Point3D(0, 0, 0)) # (1, 2, 3)
|
||||
print_point("not a point") # Not a point!
|
||||
match_list(["🤡", "ich", "hasse", "python", "manchmal"])
|
@ -1,8 +0,0 @@
|
||||
from mylist import MyList
|
||||
|
||||
lst: MyList[int] = MyList()
|
||||
lst.push_back(0)
|
||||
lst.push_back(1)
|
||||
print(lst) # [0, 1]
|
||||
lst.push_back("haha not a number")
|
||||
print(lst) # [0, 1, haha not a number]
|
@ -1,193 +0,0 @@
|
||||
# Tutorium 07 - 01.12.2023
|
||||
|
||||
## Execrise 06
|
||||
|
||||

|
||||
- Korrektur wieder am Samstag
|
||||
|
||||
### Problematik ChatGPT und Plagiate
|
||||
|
||||
- ChatGPT ist ein tolles Tool, warum?
|
||||
- Manchmal liefert es andere Lösungen zu Problemen
|
||||
- Grundverständnis bei neuen Problemen
|
||||
- integriert in die IDE (z.B. Github Copilot):
|
||||
- schneller Code schreiben
|
||||
<details>
|
||||
<summary>Wie viele Zeilen Code schreibt ein Entwickler durchschnittlich am Tag?</summary>
|
||||
<space><space><space><space>10 bis 50 Codezeilen
|
||||
</details>
|
||||
- Leichtsinnsfehler ausbessern
|
||||
- Kurz: Es nimmt einen repetetive Arbeit ab
|
||||
|
||||
#### Die Problematik?
|
||||
|
||||
- Ein EidP soll das Grundverständnis von Programmieren vermittelt werden
|
||||
- Denkweise
|
||||
- Konzepte in der theoretischen Informatik
|
||||
- Konzepte in Programmiersprachen
|
||||
- Übung
|
||||
- Um ChatGPT sinnvoll zu nutzen müsst ihr diese Grundverständnis bereits besitzen
|
||||
- Auch Studierende mit Vorwissen profitieren davon die Übung sinnvoll zu bearbeiten
|
||||
- Wenn Ihr für die Aufgaben ChatGPT verwendet, dann habt ihr nicht genug Vorwissen
|
||||
|
||||
<details>
|
||||
<summary>Studienleistung WS2022</summary>
|
||||
<img src="./img/ws2022-studienleistung.png" width=833 height=auto>
|
||||
</details>
|
||||
<details>
|
||||
<summary>Notenverteilung WS2022</summary>
|
||||
<img src="./img/ws2022-notenverteilung.png" width=833 height=auto>
|
||||
</details>
|
||||
|
||||
|
||||
#### Also, macht eure Aufgaben selber!
|
||||
|
||||
---
|
||||
|
||||
## [Advent of Code](https://adventofcode.com/)
|
||||
|
||||
- Aktuell gibt es wie jedes Jahr wieder [Advent of Code](https://adventofcode.com/).
|
||||
- Jeden Tag eine neue Aufgabe
|
||||
- Von einfach bis unmenschlich schwer (tendenziell eher einfach)
|
||||
- insane Storyline
|
||||
- [Tag 1](https://adventofcode.com/2023/day/1) ist schon da!
|
||||
- Auch sehr nice um neue Sprachen zu lernen
|
||||
- Persönliche Empfehlungen:
|
||||
- [Rust](https://www.rust-lang.org/) - Multi-paradigm mit einzigartigen Konzepten
|
||||
- [Haskell](https://www.haskell.org/) - Funktionale Programmierung
|
||||
- [Go](https://go.dev/) - OOP, High-Level Programming, sehr einfach Projekte mit aufzusetzen!
|
||||
- [Zig](https://ziglang.org/) - Low-Level Programming, rein prozedural, C-Alternative
|
||||
|
||||
----
|
||||
|
||||
## Wichtiges/Hilfreiches für Exercise-07
|
||||
|
||||
### Rekursion
|
||||
|
||||
- Rekursion in Python sind Funktionen die sich selbst aufrufen
|
||||
```python
|
||||
def fac(n: int) -> int:
|
||||
if n <= 1: # Abbruchbedingung, kein Rekursiver Aufruf mehr!
|
||||
return 1
|
||||
return n * fac(n - 1) # Rekursiver Aufruf
|
||||
```
|
||||
- Eine Rekursion braucht eine **Abbruchbedingung**
|
||||
- primitive Rekursionen können auch einfach iterative gelöst werden
|
||||
```python
|
||||
def fac2(n: int) -> int:
|
||||
fac = 1
|
||||
for i in range(1, n + 1):
|
||||
fac *= i
|
||||
return fac
|
||||
```
|
||||
- Eine Rekursion kann mehrere Rekursionspfade haben! (Kaskadenförmige Rekursion), welche auch primitiv berechenbar sind!
|
||||
```python
|
||||
def fib(n: int) -> int:
|
||||
if n in {0, 1}: # Abbruchbedingung
|
||||
return n
|
||||
return fib(n - 1) + fib(n - 2) # mehrere Rekursionsaufrufe
|
||||
```
|
||||
- Wie Funktioniert das?
|
||||
- Es wird ein Rekursionsbaum aufgebaut
|
||||
- Wenn dieser Fertig ist wird berechnet
|
||||
- Z.b. `fac`:
|
||||
```
|
||||
fac(5)
|
||||
5 * fac(4)
|
||||
5 * 4 * fac(3)
|
||||
5 * 4 * 3 * fac(2)
|
||||
5 * 4 * 3 * 2 * fac(1)
|
||||
5 * 4 * 3 * 2 * 1
|
||||
120
|
||||
```
|
||||
|
||||
```
|
||||
fib(4)
|
||||
fib(3) + fib(2)
|
||||
(fib(2) + fib(1)) + (fib(0) + fib(1))
|
||||
((fib(0) + fib(1)) + fib(1)) + (fib(0) + fib(1))
|
||||
((0 + 1) + 1) + (0 + 1)
|
||||
3
|
||||
```
|
||||
- Gibt es Rekursionen die nicht iterative berechenbar sind?
|
||||
- $\mu$-Rekursionen oder partiell Rekursionen
|
||||
- erste partiell rekursive Funktion von Wilhelm Ackermann 1926, die "Ackermannfunktion"
|
||||
|
||||
$\alpha(0, m) = m + 1$ \
|
||||
$\alpha(n, 0) = \alpha(n - 1, 1)$ \
|
||||
$\alpha(n, m) = \alpha(n, \alpha(n, m - 1))$
|
||||
|
||||
```python
|
||||
def ack(n: int, m: int) -> int:
|
||||
match (n, m):
|
||||
case (0, _):
|
||||
return m + 1
|
||||
case (_, 0):
|
||||
return ack(n - 1, 1)
|
||||
case _:
|
||||
return ack(n - 1, ack(n, m - 1))
|
||||
```
|
||||
|
||||
#### Tipp:
|
||||
|
||||
Man kann alles rekursiv Aufbauen mit Operatoren (`+, -, *, /, %, //, &&, and, ...`), also auch Listen oder Strings
|
||||
|
||||
```python
|
||||
def all_fac(max: int) -> list[tuple[int, int]]:
|
||||
if max == 0: # Abbruchbedingung
|
||||
return [(0, 1)]
|
||||
return [(max, fac(max))] + all_fac(max - 1) # Rekursion
|
||||
|
||||
def all_fac_str(min: int, max: int) -> str:
|
||||
if min >= max: # Abbruchbedingung
|
||||
return f"{fac(min)}"
|
||||
return f"{fac(min)} " + all_fac_str(min + 1, max) # Rekursion
|
||||
|
||||
def fib_str(n: int) -> str:
|
||||
if n in {0, 1}: # Abbruchbedingung
|
||||
return str(n)
|
||||
return f"({fib_str(n - 1)} + {fib_str(n - 2)})" # Rekursion
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Rekursion in Bäumen
|
||||
|
||||
- Drei möglichkeiten einen Baum *abzulaufen*
|
||||
- **Pre-Order**: Knoten, links, rechts
|
||||
```python
|
||||
def preorder[T](tree: BTree[T]):
|
||||
match tree:
|
||||
case Node(value, left, right):
|
||||
print(value)
|
||||
preorder(left)
|
||||
preorder(right)
|
||||
case _:
|
||||
return
|
||||
```
|
||||
- **Post-Order**: links, rechts, Knoten
|
||||
```python
|
||||
def postorder[T](tree: BTree[T]):
|
||||
match tree:
|
||||
case Node(value, left, right):
|
||||
postorder(left)
|
||||
postorder(right)
|
||||
print(value)
|
||||
case _:
|
||||
return
|
||||
```
|
||||
- **In-Order**: links, Knoten, rechts
|
||||
```python
|
||||
def inorder[T](tree: BTree[T]):
|
||||
match tree:
|
||||
case Node(value, left, right):
|
||||
inorder(left)
|
||||
print(value)
|
||||
inorder(right)
|
||||
case _:
|
||||
return
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Fragen?
|
Before Width: | Height: | Size: 21 KiB |
Before Width: | Height: | Size: 12 KiB |
Before Width: | Height: | Size: 10 KiB |
@ -1,57 +0,0 @@
|
||||
import random
|
||||
|
||||
|
||||
def remove_char(string: str, c: str) -> str:
|
||||
for i, s in enumerate(string):
|
||||
if s == c:
|
||||
return string[:i] + string[i + 1:]
|
||||
return string
|
||||
|
||||
|
||||
def perfect_chars(inp: str, sol: str) -> str:
|
||||
chars = ""
|
||||
for c, s in zip(inp, sol):
|
||||
if c == s:
|
||||
chars = chars + c
|
||||
return chars
|
||||
|
||||
|
||||
def correct_chars(inp: str, sol: str) -> str:
|
||||
res = ""
|
||||
for c in inp:
|
||||
if c in sol:
|
||||
res += c
|
||||
sol = remove_char(sol, c)
|
||||
return res
|
||||
|
||||
|
||||
def compare(inp: str, sol: str) -> tuple[int, int]:
|
||||
perfect = len(perfect_chars(inp, sol))
|
||||
correct = len(correct_chars(inp, sol))
|
||||
return perfect, correct - perfect
|
||||
|
||||
|
||||
def compare_alt(inp: str, sol: str) -> tuple[int, int]:
|
||||
perfect = perfect_chars(inp, sol)
|
||||
for c in perfect:
|
||||
sol = remove_char(sol, c)
|
||||
inp = remove_char(inp, c)
|
||||
correct = correct_chars(inp, sol)
|
||||
return len(perfect), len(correct)
|
||||
|
||||
|
||||
def game(length: int, symbols: str):
|
||||
solution = "".join(random.choices(symbols, k=length))
|
||||
print("Länge:", length, "Zeichen:", symbols)
|
||||
result = (0, 0)
|
||||
while result != (length, 0):
|
||||
c = input()
|
||||
if len(c) != length:
|
||||
print("Try again!")
|
||||
continue
|
||||
result = compare(c, solution)
|
||||
print("Antwort:", result[0] * "X" + result[1] * "-")
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
game(5, "ABCDE")
|
@ -1,35 +0,0 @@
|
||||
from typing import Optional
|
||||
|
||||
|
||||
def head[T](xs: list[T]) -> Optional[T]:
|
||||
if not xs:
|
||||
return None
|
||||
return xs[0]
|
||||
|
||||
|
||||
def tail[T](xs: list[T]) -> Optional[list[T]]:
|
||||
if not xs:
|
||||
return None
|
||||
return xs[1:]
|
||||
|
||||
|
||||
def concat[T](xss: list[list[T]]) -> list[T]:
|
||||
outer = list()
|
||||
for xs in xss:
|
||||
outer += xs
|
||||
return outer
|
||||
|
||||
|
||||
def zip[T, U](xs: list[T], ys: list[U]) -> list[tuple[T, U]]:
|
||||
out = list()
|
||||
for i, x in enumerate(xs):
|
||||
if i >= len(ys):
|
||||
return out
|
||||
else:
|
||||
out += [(x, ys[i])]
|
||||
return out
|
||||
|
||||
|
||||
def assoc[T, U, V](t: tuple[tuple[T, U], V]) -> tuple[T, tuple[U, V]]:
|
||||
(x, y), z = t
|
||||
return (x, (y, z))
|
@ -1,36 +0,0 @@
|
||||
@dataclass
|
||||
class Node[T]:
|
||||
value: T
|
||||
left: Optional['Node[T]'] = None
|
||||
right: Optional['Node[T]'] = None
|
||||
|
||||
type BTree[T] = Node[T] | None
|
||||
|
||||
def preorder[T](tree: BTree[T]):
|
||||
match tree:
|
||||
case Node(value, left, right):
|
||||
print(value)
|
||||
preorder(left)
|
||||
preorder(right)
|
||||
case _:
|
||||
return
|
||||
|
||||
|
||||
def postorder[T](tree: BTree[T]):
|
||||
match tree:
|
||||
case Node(value, left, right):
|
||||
postorder(left)
|
||||
postorder(right)
|
||||
print(value)
|
||||
case _:
|
||||
return
|
||||
|
||||
|
||||
def inorder[T](tree: BTree[T]):
|
||||
match tree:
|
||||
case Node(value, left, right):
|
||||
inorder(left)
|
||||
print(value)
|
||||
inorder(right)
|
||||
case _:
|
||||
return
|
@ -1,55 +0,0 @@
|
||||
def fibonacci(n: int) -> int:
|
||||
if n in {0, 1}: # Abbruchbedingung
|
||||
return n
|
||||
return fibonacci(n - 1) + fibonacci(n - 2) # mehrere Rekursionsaufrufe
|
||||
|
||||
|
||||
def fac(n: int) -> int:
|
||||
if n <= 0: # Abbruchbedingung, kein Rekursiver Aufruf mehr!
|
||||
return 1
|
||||
return n * fac(n - 1) # Rekursiver Aufruf
|
||||
|
||||
|
||||
def fac2(n: int) -> int:
|
||||
num = 1
|
||||
for i in range(1, n + 1):
|
||||
num *= i
|
||||
return num
|
||||
|
||||
|
||||
def ack(n: int, m: int) -> int:
|
||||
match (n, m):
|
||||
case (0, _):
|
||||
return m + 1
|
||||
case (_, 0):
|
||||
return ack(n - 1, 1)
|
||||
case _:
|
||||
return ack(n - 1, ack(n, m - 1))
|
||||
|
||||
|
||||
def all_fac(max: int) -> list[tuple[int, int]]:
|
||||
if max == 0: # Abbruchbedingung
|
||||
return [(0, 1)]
|
||||
return [(max, fac(max))] + all_fac(max - 1) # Rekursion
|
||||
|
||||
|
||||
def all_fac_str(min: int, max: int) -> str:
|
||||
if min >= max: # Abbruchbedingung
|
||||
return f"{fac(min)}"
|
||||
return f"{fac(min)} " + all_fac_str(min + 1, max) # Rekursion
|
||||
|
||||
|
||||
def fib_str(n: int) -> str:
|
||||
if n in {0, 1}:
|
||||
return str(n)
|
||||
return f"({fib_str(n - 1)} + {fib_str(n - 2)})"
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
assert [fibonacci(n) for n in range(15)] == [0, 1, 1, 2,
|
||||
3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377]
|
||||
assert [fac(n) for n in range(11)] == [1, 1, 2, 6, 24,
|
||||
120, 720, 5040, 40320, 362880, 3628800]
|
||||
assert [fac(n) for n in range(10)] == [fac2(n) for n in range(10)]
|
||||
assert list(reversed(all_fac(10))) == [(n, fac(n)) for n in range(11)]
|
||||
assert all_fac_str(0, 10) == "1 1 2 6 24 120 720 5040 40320 362880 3628800"
|
@ -1,589 +0,0 @@
|
||||
---
|
||||
marp: true
|
||||
paginate: true
|
||||
class: invert
|
||||
# theme: uncover
|
||||
footer: Tutorium 08 - 08.12.2023 - Nils Pukropp - https://s.narl.io/s/tutorium-08
|
||||
header:
|
||||
---
|
||||
|
||||
|
||||
# Tutorium 08
|
||||
|
||||
Exercise 07 - Korrektur
|
||||
Objekt-orientierte Programmierung
|
||||
Exercise 08 - Hilfe und Fragen
|
||||
|
||||
---
|
||||
|
||||
# Exercise 07 - Musterlösung
|
||||
|
||||
---
|
||||
|
||||
# Aufgabe 7.1 a)
|
||||
|
||||
```python
|
||||
def ask(s: str) -> Optional[bool]:
|
||||
match input(f"{s}? [Yes / No]: "):
|
||||
case "Yes" | "yes":
|
||||
return True
|
||||
case "No" | "no":
|
||||
return False
|
||||
case _:
|
||||
return None
|
||||
```
|
||||
|
||||
- Default-Case `case _:` oder exhaustive pattern matching
|
||||
- Keinen unnötigen Code-Duplikat mit `|`
|
||||
|
||||
---
|
||||
|
||||
# Aufgabe 7.1 b)
|
||||
|
||||
```python
|
||||
class Operator(Enum):
|
||||
ADD = auto()
|
||||
MUL = auto()
|
||||
|
||||
|
||||
def eval[T: (int, str)](t: tuple[Operator, T, T]) -> Optional[T]:
|
||||
match t:
|
||||
case (Operator.ADD, x, y):
|
||||
return x + y
|
||||
case (Operator.MUL, int(i), int(j)):
|
||||
return i * j
|
||||
case _:
|
||||
return None
|
||||
```
|
||||
- Default-Case `case _:` oder exhaustive pattern matching
|
||||
- Generic `T` mit der Einschränkung `T: (int, str)` (nicht `int | str`!)
|
||||
|
||||
---
|
||||
|
||||
# Aufgabe 7.1 c)
|
||||
|
||||
```python
|
||||
@dataclass
|
||||
class Cons[T]:
|
||||
head: T
|
||||
tail: Optional["Cons[T]"] = None
|
||||
|
||||
|
||||
type LList[T] = Optional[Cons[T]]
|
||||
```
|
||||
Hierbei gab es einige Verwirrung, was wahrscheinlich unter anderem an der Benennung lag
|
||||
|
||||
---
|
||||
|
||||
# Aufgabe 7.1 c)
|
||||
|
||||
```python
|
||||
@dataclass
|
||||
class Node[T]:
|
||||
value: T
|
||||
next: Optional['Node[T]'] = None
|
||||
|
||||
|
||||
type LinkedList[T] = Optional[Node[T]]
|
||||
```
|
||||
|
||||
Wobei jede `Node` auch einen Wert vom Typ `T` hat und weiß was die nächste `Node` in der Liste ist. Also ist `Node` quasi ein Eintrag in der Liste, der sich merkt was als nächstes kommt und einem Wert zugeordnet wird.
|
||||
|
||||
---
|
||||
|
||||
# Aufgabe 7.1 c)
|
||||
|
||||
```python
|
||||
def tail[T](xs: LList[T]) -> LList[T]:
|
||||
match xs:
|
||||
case None:
|
||||
return None
|
||||
case Cons(_, tail):
|
||||
return tail
|
||||
|
||||
def len(xs: LList[Any]) -> int:
|
||||
match xs:
|
||||
case None:
|
||||
return 0
|
||||
case Cons(_, tail):
|
||||
return 1 + len(tail)
|
||||
|
||||
```
|
||||
|
||||
|
||||
---
|
||||
|
||||
das geht aber auch schöner:
|
||||
|
||||
```python
|
||||
def next[T](xs: LinkedList[T]) -> LinkedList[T]:
|
||||
return xs.next if xs else xs
|
||||
|
||||
def len(xs: LinkedList[Any]) -> int:
|
||||
return 1 + len(xs.next) if xs else 0
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
# Aufgabe 7.2 - BTree Definition
|
||||
|
||||
```python
|
||||
@dataclass
|
||||
class Node[T]:
|
||||
mark: T
|
||||
left: Optional["Node[T]"] = None
|
||||
right: Optional["Node[T]"] = None
|
||||
|
||||
|
||||
type BTree[T] = Optional[Node[T]]
|
||||
```
|
||||
- Binärbaum
|
||||
- generisch und rekursiv definiert wie unsere `LList[T]`
|
||||
|
||||
---
|
||||
|
||||
# Aufgabe 7.2 a)
|
||||
|
||||
```python
|
||||
def contains[T](tree: BTree[T], val: T) -> bool:
|
||||
return tree and (tree.m == val or contains(tree.left, val) or contains(tree.right, val))
|
||||
```
|
||||
- wir testen ob der aktuelle `tree` eben `None` ist
|
||||
- dann testen wir ob der aktuelle Wert unserem gesuchten entspricht
|
||||
- Wenn das nicht der Fall ist laufen wir den Baum rekursiv ab
|
||||
- Wichtig ist dass man Links **und** Rechts abläuft, und nicht einen Zweig vergisst
|
||||
|
||||
---
|
||||
|
||||
# Aufgabe 7.2 b)
|
||||
|
||||
```python
|
||||
def leaves[T](tree: BTree[T]) -> list[T]:
|
||||
match tree:
|
||||
case None:
|
||||
return []
|
||||
case Node(mark, None, None):
|
||||
return [mark]
|
||||
case Node(_, left, right):
|
||||
return leaves(left) + leaves(right)
|
||||
```
|
||||
- exhaustive matchen oder default-case!
|
||||
|
||||
---
|
||||
|
||||
# Aufgabe 7.2 - AST Definition
|
||||
|
||||
```python
|
||||
type AST = BTree[int | str]
|
||||
```
|
||||
- Schlechtes Design, weil `str` kann alles sein, wir gehen nur davon aus dass es `"+"` oder `"*"` ist.
|
||||
- Man hätte auch einfach den `Enum` aus Aufgabe 1 verwenden können...
|
||||
|
||||
```python
|
||||
class Op(Enum):
|
||||
ADD = "+"
|
||||
MUL = "*"
|
||||
|
||||
type AST = BTree[int | Op]
|
||||
```
|
||||
---
|
||||
|
||||
# Aufgabe 7.2 c)
|
||||
|
||||
```python
|
||||
def evaluate(tree: AST) -> Optional[int]:
|
||||
match tree:
|
||||
case Node(int(i), None, None):
|
||||
return i
|
||||
case Node("*" | "+", left, right):
|
||||
left = evaluate(left)
|
||||
right = evaluate(right)
|
||||
if left is None or right is None:
|
||||
return None
|
||||
if tree.mark == "+":
|
||||
return left + right
|
||||
else:
|
||||
return left * right
|
||||
case _:
|
||||
return None
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
```python
|
||||
def evaluate(tree: AST) -> Optional[int]:
|
||||
match tree:
|
||||
case Node(int(i), None, None):
|
||||
return i
|
||||
case Node(Op.MUL | Op.ADD, left, right) if left and right:
|
||||
left = evaluate(left)
|
||||
right = evaluate(right)
|
||||
if tree.mark == Op.ADD:
|
||||
return left + right
|
||||
else:
|
||||
return left * right
|
||||
case _:
|
||||
return None
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
# Aufgabe 7.2 d)
|
||||
|
||||
```python
|
||||
def infix_str(tree: AST) -> str:
|
||||
match tree:
|
||||
case Node(int(i), _, _):
|
||||
return str(i)
|
||||
case Node(str(s), left, right):
|
||||
return f"({infix_str(left)} {s} {infix_str(right)})"
|
||||
case _:
|
||||
return ""
|
||||
```
|
||||
|
||||
- Das wichtigste war die Reihenfolge der Rekursiven aufrufe.
|
||||
- Hier also `infix_str(left) + s + infix_str(right)`
|
||||
|
||||
---
|
||||
|
||||
```python
|
||||
def prefix_str(tree: AST) -> str:
|
||||
match tree:
|
||||
case Node(int(i), _, _):
|
||||
return str(i)
|
||||
case Node(str(s), left, right):
|
||||
return f"({s} {prefix_str(left)} {prefix_str(right)})"
|
||||
case _:
|
||||
return ""
|
||||
|
||||
def postfix_str(tree: AST) -> str:
|
||||
match tree:
|
||||
case Node(int(i), _, _):
|
||||
return str(i)
|
||||
case Node(str(s), left, right):
|
||||
return f"({postfix_str(left)} {postfix_str(right)} {s})"
|
||||
case _:
|
||||
return ""
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
# Fragen?
|
||||
|
||||
---
|
||||
|
||||
# Probleme bei Exercise 07
|
||||
|
||||
- Generics `T` wurden unnötig verwendet
|
||||
- Ihr müsst keine Generics verwenden wenn nicht nötig
|
||||
- Sowas wie `T: int` ist unnötig, schreibt einfach direkt `int`
|
||||
- Rekursion als Konzept - dabei hilft nur Üben
|
||||
- begegnet einem aber auch unglaublich selten in der echten Welt
|
||||
- wenn es Verständnis-Fragen gibt einfach melden
|
||||
- `@dataclass` mit `Enum` verwendet (dabei geht alles kaputt)
|
||||
|
||||
---
|
||||
|
||||
# Objekt orientiertes Programmieren - OOP
|
||||
|
||||
---
|
||||
|
||||
# Definitionen in der OOP - Klassen
|
||||
|
||||
- Eine Klasse ist wie ein Bauplan
|
||||
- Jede Klasse definiert die Eigenschaften und das Verhalten
|
||||
- Verhalten sind Methoden also `def`
|
||||
- Eigenschaften sind Attribute, `int`, `float`, `str`, `list`, ...
|
||||
- Die Eigenschaften definieren den Zustand
|
||||
- Eigenschaften können sich ändern
|
||||
|
||||
---
|
||||
|
||||
# Beispiel - Cat
|
||||
|
||||
```python
|
||||
from dataclasses import dataclass
|
||||
|
||||
@dataclass
|
||||
class Cat:
|
||||
age: int
|
||||
weight: float
|
||||
name: str
|
||||
|
||||
def meow(self):
|
||||
print("Meow")
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
# Beispiel - Dog
|
||||
|
||||
```python
|
||||
from dataclasses import dataclass
|
||||
|
||||
@dataclass
|
||||
class Dog:
|
||||
age: int
|
||||
weight: float
|
||||
name: str
|
||||
|
||||
def woof(self):
|
||||
print("Woof")
|
||||
```
|
||||
---
|
||||
|
||||
# Objekte erzeugen
|
||||
|
||||
```python
|
||||
dog = Dog(age=3, weight=50.0, name="dog")
|
||||
cat = Cat(age=7, weight=4.5, name="cat")
|
||||
dog.woof() # Woof
|
||||
cat.meow() # Meow
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
# Objekte erzeugen
|
||||
|
||||
```python
|
||||
dog = Dog(3, 50.0, "dog")
|
||||
cat = Cat(7, 4.5, "cat")
|
||||
dog.woof() # Woof
|
||||
cat.meow() # Meow
|
||||
```
|
||||
# Zustand ändern
|
||||
```python
|
||||
cat.age = 8
|
||||
print(cat.age) # 8
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
# Vererbung
|
||||
|
||||
- Was haben `Cat` und `Dog` gemeinsam?
|
||||
- `age`
|
||||
- `weight`
|
||||
- `name`
|
||||
|
||||
=> Lösung ist eine Oberklasse `Animal`
|
||||
|
||||
---
|
||||
|
||||
# Vererbung - Animal
|
||||
|
||||
```python
|
||||
@dataclass
|
||||
class Animal:
|
||||
age: int
|
||||
weight: int
|
||||
name: str
|
||||
```
|
||||
|
||||
```python
|
||||
@dataclass
|
||||
class Cat(Animal):
|
||||
pass
|
||||
|
||||
@dataclass
|
||||
class Dog(Animal):
|
||||
pass
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
# Bisher in der Vorlesung
|
||||
|
||||
```python
|
||||
def make_noise(animal: Animal):
|
||||
match animal:
|
||||
case Dog():
|
||||
print("Woof")
|
||||
case Cat():
|
||||
print("Meow")
|
||||
case _:
|
||||
pass
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
# Bisher in der Vorlesung
|
||||
|
||||
```python
|
||||
def make_noise(animal: Animal):
|
||||
match animal:
|
||||
case Dog():
|
||||
print("Woof")
|
||||
case Cat():
|
||||
print("Meow")
|
||||
case _:
|
||||
pass
|
||||
```
|
||||
|
||||
das ist kein gutes Design, warum?
|
||||
|
||||
---
|
||||
|
||||
was ist wenn wir jetzt eine neue Klasse `Mouse` erstellen wollen.
|
||||
|
||||
```python
|
||||
@dataclass
|
||||
class Mouse(Animal):
|
||||
pass
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
jetzt müssen wir `Mouse` zu `make_noise` hinzufügen
|
||||
|
||||
```python
|
||||
def make_noise(animal: Animal):
|
||||
match animal:
|
||||
case Dog():
|
||||
print("Woof")
|
||||
case Cat():
|
||||
print("Meow")
|
||||
case Mouse():
|
||||
print("Peep")
|
||||
case _:
|
||||
pass
|
||||
```
|
||||
|
||||
das kann ziemlich nervig werden. Vor allem wenn wir größere Projekte haben
|
||||
|
||||
---
|
||||
|
||||
# Polymorphism
|
||||
|
||||
> Die Polymorphie der objektorientierten Programmierung ist eine Eigenschaft, die immer im Zusammenhang mit Vererbung und Schnittstellen (Interfaces) auftritt. Eine Methode ist polymorph, wenn sie in verschiedenen Klassen die gleiche Signatur hat, jedoch erneut implementiert ist.
|
||||
|
||||
---
|
||||
|
||||
```python
|
||||
@dataclass
|
||||
class Animal:
|
||||
age: int
|
||||
weight: float
|
||||
name: str
|
||||
|
||||
def make_noise(self) -> None:
|
||||
pass
|
||||
|
||||
@dataclass
|
||||
class Cat(Animal):
|
||||
|
||||
def make_noise(self) -> None:
|
||||
print("Meow")
|
||||
|
||||
@dataclass
|
||||
class Dog(Animal):
|
||||
|
||||
def make_noise(self) -> None:
|
||||
print("Woof")
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
# Geht das schöner in Python 3.12?
|
||||
|
||||
Ja, mit `override` sagt sogar pylance bescheid wenn wir eine polymorphe Methode falsch überschrieben haben!
|
||||
```python
|
||||
form dataclasses import dataclass
|
||||
from typing import override
|
||||
|
||||
@dataclass
|
||||
class Cat(Animal):
|
||||
|
||||
@override
|
||||
def make_noise(self) -> None:
|
||||
print("Meow")
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
# Unsere neue polymorphe Methode benutzen
|
||||
|
||||
```python
|
||||
@dataclass
|
||||
class Zoo:
|
||||
animals: list[Animal] = []
|
||||
|
||||
|
||||
zoo = Zoo(animals=[
|
||||
Cat(age=6, weight=4.5, name="Milow"),
|
||||
Cat(age=7, weight=5.0, name="Blacky"),
|
||||
Dog(age=12, weight=40.3, name="Bernd")
|
||||
])
|
||||
|
||||
for animal in zoo.animals:
|
||||
animal.make_noise() # Meow
|
||||
# Meow
|
||||
# Woof
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
# Oberklasse Animal
|
||||
|
||||
- Was passiert wenn man `Animal` euzeugt?
|
||||
- Wir haben ein Objekt was eigentlich nicht erzeugt werden sollte
|
||||
- Es ist eine Art Schablone für alle Klassen von Typ `Animal`
|
||||
|
||||
Können wir die Instanziierung verhindern? Ja!
|
||||
|
||||
---
|
||||
|
||||
# Abstrakte Klassen
|
||||
|
||||
- abstrakte Klassen sind kein konkreter Bauplan für ein Objekt
|
||||
- stattdessen eine Schablone für weiter Baupläne
|
||||
- `Animal` ist so einen Schablone für alle Tiere
|
||||
|
||||
---
|
||||
|
||||
# Abstrakte Klassen - in anderen Programmiersprachen
|
||||
Beispiel Java mit `abstract`
|
||||
```java
|
||||
abstract class Animal {
|
||||
private int age;
|
||||
private float weight;
|
||||
private String name;
|
||||
|
||||
abstract void make_noise();
|
||||
}
|
||||
|
||||
class Cat extends Animal {
|
||||
@override
|
||||
void make_noise() {
|
||||
System.out.println("Meow")
|
||||
}
|
||||
}
|
||||
```
|
||||
---
|
||||
|
||||
# Abstrakte Klassen - Python
|
||||
|
||||
- `ABC` aus dem Modul `abc`.
|
||||
- Ja sie mussten `abstract` wirklich abkürzen mit `ABC` (Abstract Base Class)
|
||||
- für abstrakte Methoden gibt es die Annotation `@abstractmethod` im selben Modul
|
||||
|
||||
```python
|
||||
from abc import ABC, abstractmethod
|
||||
|
||||
class Animal(ABC):
|
||||
age: int
|
||||
weight: float
|
||||
name: str
|
||||
|
||||
@abstractmethod
|
||||
def make_noise():
|
||||
pass
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
# Fragen?
|
||||
|
||||
---
|
||||
|
||||
# Exercise 08
|
@ -1,514 +0,0 @@
|
||||
---
|
||||
marp: true
|
||||
paginate: true
|
||||
class: invert
|
||||
# theme: uncover
|
||||
footer: Tutorium 09 - 16.12.2023 - Nils Pukropp - https://s.narl.io/s/tutorium-09
|
||||
header:
|
||||
---
|
||||
|
||||
# Tutorium 09
|
||||
|
||||
Korrektur 08 - Vererbung, OOP, Datenkapselung
|
||||
|
||||
---
|
||||
|
||||
# Korrektur 08
|
||||
|
||||
---
|
||||
|
||||
# Punkteverteilung
|
||||
|
||||

|
||||
|
||||
---
|
||||
|
||||
# Häufige Fehler
|
||||
|
||||
* `@dataclass` nicht verwendet
|
||||
* `__init__` überschrieben, obwohl `@dataclass` das macht und dann `super().__init__()` vergessen
|
||||
* Kein Polymorphismus verwendet, also Code Duplikate oder auf `self` gematched/`isinstance()` verwendet
|
||||
* Code nicht getestet, Datei nicht ausführbar => **0 Punkte**!
|
||||
|
||||
---
|
||||
|
||||
# Musterlösung - Aufgabe 8 a,b)
|
||||
|
||||
```py
|
||||
import math
|
||||
from dataclasses import dataclass
|
||||
|
||||
|
||||
@dataclass
|
||||
class Vec2:
|
||||
x: float
|
||||
y: float
|
||||
|
||||
def abs(self) -> float:
|
||||
return math.sqrt(self.x * self.x + self.y * self.y)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
# Musterlösung - Aufgabe 8 c)
|
||||
|
||||
```py
|
||||
@dataclass
|
||||
class GameObject:
|
||||
position: Vec2
|
||||
radius: int
|
||||
alive: bool
|
||||
color: tuple[int, int, int]
|
||||
|
||||
@dataclass
|
||||
class Projectile(GameObject):
|
||||
speed: float
|
||||
|
||||
@dataclass
|
||||
class StaticObject(GameObject):
|
||||
rotation: float
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
# Musterlösung - Aufgabe 8 c)
|
||||
|
||||
```py
|
||||
class Item(StaticObject):
|
||||
amount: int
|
||||
|
||||
class Ammunition(Item):
|
||||
pass
|
||||
|
||||
class Health(Item):
|
||||
pass
|
||||
|
||||
class Ship(GameObject):
|
||||
shots: int
|
||||
hp: int
|
||||
|
||||
class Asteroid(StaticObject):
|
||||
special: bool
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
# Musterlösung - Aufgabe 8 d)
|
||||
|
||||
```python
|
||||
class GameObject:
|
||||
|
||||
# ...
|
||||
|
||||
def update(self, width: int, height: int, delta: float):
|
||||
if not (0 <= self.position.x < width and 0 <= self.position.y < height):
|
||||
self.alive = False
|
||||
|
||||
class Projectile(GameObject):
|
||||
speed: float
|
||||
|
||||
def update(self, width: int, height: int, delta: float):
|
||||
self.position.y -= delta * self.speed
|
||||
super().update(width, height, delta)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
# Musterlösung - Aufgabe 8 d)
|
||||
|
||||
```python
|
||||
class StaticObject(GameObject):
|
||||
rotation: float
|
||||
|
||||
def update(self, width: int, height: int, delta: float):
|
||||
self.position.y += delta
|
||||
self.rotation += delta / self.radius
|
||||
super().update(width, height, delta)
|
||||
|
||||
class Ship(GameObject):
|
||||
shots: int
|
||||
hp: int
|
||||
|
||||
def update(self, width: int, height: int, delta: float):
|
||||
if self.hp <= 0:
|
||||
self.hp = 0
|
||||
self.alive = False
|
||||
super().update(width, height, delta)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
# Musterlösung - Aufgabe 8 e)
|
||||
|
||||
```python
|
||||
@dataclass
|
||||
class GameObject:
|
||||
|
||||
# ...
|
||||
|
||||
def is_colliding(self, other: "GameObject") -> bool:
|
||||
dist = Vec2(self.position.x - other.position.x, self.position.y - other.position.y)
|
||||
return dist.abs() <= self.radius + other.radius
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
# Musterlösung - Aufgabe 8 f)
|
||||
|
||||
```python
|
||||
class GameObject:
|
||||
|
||||
# ...
|
||||
|
||||
def on_collision(self, other: "GameObject"):
|
||||
pass
|
||||
|
||||
class Projectile(GameObject):
|
||||
|
||||
# ...
|
||||
|
||||
def on_collision(self, other: 'GameObject'):
|
||||
if not isinstance(other, Ship):
|
||||
self.alive = False
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
# Musterlösung - Aufgabe 8 f)
|
||||
|
||||
```python
|
||||
class StaticObject(GameObject):
|
||||
|
||||
# ...
|
||||
|
||||
def on_collision(self, other: 'GameObject'):
|
||||
self.alive = False
|
||||
|
||||
class Ship(GameObject):
|
||||
# ...
|
||||
def on_collision(self, other: 'GameObject'):
|
||||
match other:
|
||||
case Asteroid():
|
||||
self.hp -= other.radius
|
||||
case Health():
|
||||
self.hp += other.amount
|
||||
case Ammunition():
|
||||
self.shots += other.amount
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
# Musterlösung - Aufgabe 8 f)
|
||||
|
||||
```python
|
||||
|
||||
@dataclass
|
||||
class Asteroid(StaticObject):
|
||||
special: bool
|
||||
|
||||
def on_collision(self, other: 'GameObject'):
|
||||
if not isinstance(other, Asteroid):
|
||||
self.alive = False
|
||||
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
# Musterlösung - Aufgabe 8 g)
|
||||
|
||||
```python
|
||||
@dataclass
|
||||
class Ship(GameObject):
|
||||
|
||||
# ...
|
||||
|
||||
def shoot(self) -> Projectile:
|
||||
alive = False
|
||||
if self.shots:
|
||||
self.shots -= 1
|
||||
alive = True
|
||||
pos = Vec2(self.position.x, self.position.y)
|
||||
return Projectile(pos, 5, alive, (255, 0, 0), 3)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
# Musterlösung - Aufgabe 8 h)
|
||||
|
||||
```python
|
||||
@dataclass
|
||||
class GameObject:
|
||||
|
||||
# ...
|
||||
|
||||
def draw(self, screen: pygame.Surface):
|
||||
pygame.draw.circle(screen, self.color, (self.position.x, self.position.y), self.radius)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
# Override-Dekorator
|
||||
|
||||
- ist in `typing`
|
||||
- Wird über Methoden geschrieben, die überschrieben werden
|
||||
- Pylance zeigt einen Fehler an, wenn die überschriebene Methode in keiner Oberklasse gefunden wird
|
||||
- Hilft Fehler vorzubeugen - falsche Signatur, Parameter, ...
|
||||
|
||||
---
|
||||
|
||||
# Override-Dekorator - Beispiel
|
||||
```python
|
||||
from typing import override
|
||||
from dataclasses import dataclass
|
||||
|
||||
|
||||
@dataclass
|
||||
class GameObject:
|
||||
|
||||
def on_collision(self, other: 'GameObject'):
|
||||
pass
|
||||
|
||||
class StaticObject(GameObject):
|
||||
|
||||
@override
|
||||
def on_collisoin(self, other: 'GameObject'): # Pylance-Error
|
||||
self.alive = False
|
||||
```
|
||||
---
|
||||
|
||||
# Override-Dekorator - Beispiel
|
||||
```python
|
||||
from typing import override
|
||||
from dataclasses import dataclass
|
||||
|
||||
|
||||
@dataclass
|
||||
class GameObject:
|
||||
|
||||
def on_collision(self, other: 'GameObject'):
|
||||
pass
|
||||
|
||||
class StaticObject(GameObject):
|
||||
|
||||
@override
|
||||
def on_collision(self): # Pylance-Error
|
||||
self.alive = False
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
# Datenkapselung
|
||||
|
||||
- Man möchte manche Implementierung verstecken
|
||||
- Wenn andere deinen Code verwenden, dann möchte man eine Schnittstelle anbieten die intuitiv ist.
|
||||
|
||||
```python
|
||||
@dataclass
|
||||
class MyList[T]:
|
||||
internal_list: list[T] = []
|
||||
|
||||
def add(self, item: T) -> None:
|
||||
self.internal_list += [other]
|
||||
```
|
||||
---
|
||||
# Datenkapselung - warum ist das schlecht?
|
||||
|
||||
```python
|
||||
from my_collections import MyList
|
||||
|
||||
xs = MyList()
|
||||
xs.internal_list # ????
|
||||
```
|
||||
|
||||
- was sollen wir mit `internal_list`?
|
||||
- andere sollten nur auf `add()` zugreifen können
|
||||
|
||||
---
|
||||
|
||||
# Private Attribute
|
||||
|
||||
```python
|
||||
@dataclass
|
||||
class MyList[T]:
|
||||
_internal_list: InitVar[list[T]]
|
||||
_length: InitVar[int]
|
||||
|
||||
def __init__(self):
|
||||
self.__internal_list = []
|
||||
self.__length = 0
|
||||
|
||||
def add(self, item: T):
|
||||
self.__internal_list += [item]
|
||||
self.__length += 1
|
||||
|
||||
@property
|
||||
def length(self) -> int:
|
||||
return self.__length
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
# Private Attribute
|
||||
|
||||
```python
|
||||
@dataclass
|
||||
class MyList[T]:
|
||||
_internal_list: InitVar[list[T]]
|
||||
_length: InitVar[int]
|
||||
|
||||
def __init__(self):
|
||||
self.__internal_list = []
|
||||
self.__length = 0
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
# Private Attribute
|
||||
|
||||
```python
|
||||
@dataclass
|
||||
class MyList[T]:
|
||||
_internal_list: InitVar[list[T]]
|
||||
_length: InitVar[int]
|
||||
|
||||
def __init__(self):
|
||||
self.__internal_list = []
|
||||
self.__length = 0
|
||||
|
||||
def add(self, item: T):
|
||||
self.__internal_list += [item]
|
||||
self.__length += 1
|
||||
|
||||
@property
|
||||
def length(self) -> int:
|
||||
return self.__length
|
||||
```
|
||||
---
|
||||
|
||||
# Private Attribute - Setter
|
||||
|
||||
- Manchmal wollen wir trotzdem private Attribute setzen
|
||||
- Aber vielleicht nur wenn bestimmte Bedingungen erfüllt sind
|
||||
|
||||
```python
|
||||
class GameObject:
|
||||
_position: InitVar[tuple[int, int]]
|
||||
|
||||
def __post__init__(self, position: tuple[int, int]):
|
||||
assert (0, 0) <= position
|
||||
self.__position = position
|
||||
|
||||
@property
|
||||
def position(self) -> tuple[int, int]:
|
||||
return self.__position
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
# Private Attribute - Setter
|
||||
|
||||
```python
|
||||
@dataclass
|
||||
class GameObject:
|
||||
_position: InitVar[tuple[int, int]]
|
||||
|
||||
def __post_init__(self, position: tuple[int, int]):
|
||||
assert (0, 0) > position
|
||||
self.__position = position
|
||||
|
||||
@property
|
||||
def position(self) -> tuple[int, int]:
|
||||
return self.__position
|
||||
|
||||
@position.setter
|
||||
def position(self, position: tuple[int, int]):
|
||||
if (0, 0) > position:
|
||||
return
|
||||
self.__position = position
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
# Comprehensive-Guide to `class`
|
||||
|
||||
## `@dataclass`
|
||||
|
||||
- Attribute werden im Klassenrumpf definiert
|
||||
- können mit einem Standardwert definiert werden
|
||||
- `__init__`, `__post_init__`, `__repr__`, `__eq__`, `__lt__`, `__le__`, `__gt__`, `__ge__`, ... werden automatisch generiert
|
||||
- In der Vorlesung benutzen wir nur `dataclass`
|
||||
|
||||
---
|
||||
|
||||
# Comprehensive-Guide to `class`
|
||||
|
||||
## `@dataclass`
|
||||
|
||||
- Attribute die im Klassenrumpf definiert werden, werden automatisch in die `__init__` generiert, auch wenn es einen Standardwert gibt!
|
||||
|
||||
```python
|
||||
@dataclass
|
||||
class A:
|
||||
x: int
|
||||
y: int = 0
|
||||
|
||||
def __init__(self, x: int, y: int = 0): # das macht @dataclass von selber!
|
||||
self.x = x
|
||||
self.y = y
|
||||
```
|
||||
---
|
||||
|
||||
# Comprehensive-Guide to `class`
|
||||
|
||||
## `Enum`
|
||||
|
||||
- Wenn man eine endliche Aufzählung braucht (endliche Menge)
|
||||
- macht die Fallunterscheidung einfach weil es endliche Elemente gibt
|
||||
- Versichert auch dass kein quatsch übergeben wird wie zb bei `str`
|
||||
- **niemals** mit `@dataclass`, sonst geht alles kaputt
|
||||
|
||||
---
|
||||
|
||||
# Enum - Beispiel
|
||||
|
||||
```python
|
||||
def eval[T: (int | float)](operator: str, x: T, y: T) -> T:
|
||||
match operator:
|
||||
case '+':
|
||||
return x + y
|
||||
case '-':
|
||||
return x - y
|
||||
case '*':
|
||||
return x * y
|
||||
case '/' if y != 0:
|
||||
return x / y
|
||||
case _:
|
||||
return 0
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
# Enum - Beispiel
|
||||
|
||||
```python
|
||||
from enum import Enum, auto
|
||||
|
||||
class Op(Enum):
|
||||
ADD = auto()
|
||||
SUB = auto()
|
||||
DIV = auto()
|
||||
MUL = auto()
|
||||
```
|
||||
Jetzt passen wir die Methodensignatur an
|
||||
```python
|
||||
def eval[T: (int | float)](operator: Op, x: T, y: T) -> T:
|
||||
```
|
||||
Jetzt kann nichts beliebiges als `operator` übergeben werden
|
||||
|
||||
---
|
||||
|
||||
# Blatt 09 - Fragen?
|
||||
|
||||
- Abgabe: 18.12. - 09:00
|
||||
- Testet euren Code!
|
||||
- Es gibt keine dummen Fragen wenns ums Verständnis geht
|
Before Width: | Height: | Size: 20 KiB |
Before Width: | Height: | Size: 18 KiB |
@ -1,31 +0,0 @@
|
||||
from dataclasses import dataclass
|
||||
|
||||
@dataclass
|
||||
class A:
|
||||
x: int
|
||||
y: int = 0
|
||||
|
||||
# die init die von `dataclass` generiert wird, also unnötig
|
||||
# siehe class B
|
||||
def __init__(self, x: int, y: int = 0):
|
||||
self.x = x
|
||||
self.y = y
|
||||
|
||||
@dataclass
|
||||
class B:
|
||||
x: int
|
||||
y: int = 0
|
||||
|
||||
if __name__ == '__main__':
|
||||
my_a = A(1)
|
||||
assert my_a.x == 1
|
||||
assert my_a.y == 0
|
||||
my_other_a = A(1, y = 1)
|
||||
assert my_other_a.x == 1
|
||||
assert my_other_a.y == 1
|
||||
my_b = B(1)
|
||||
assert my_b.x == 1
|
||||
assert my_b.y == 0
|
||||
my_other_b = B(1, y = 1)
|
||||
assert my_other_b.x == 1
|
||||
assert my_other_b.y == 1
|
@ -1,56 +0,0 @@
|
||||
from dataclasses import dataclass, InitVar
|
||||
|
||||
|
||||
@dataclass
|
||||
class MyList[T]:
|
||||
_internal_list: InitVar[list[T]]
|
||||
_length: InitVar[int]
|
||||
|
||||
def __init__(self) -> None:
|
||||
self.__internal_list: list[T] = []
|
||||
self.__length = 0
|
||||
|
||||
def add(self, item: T) -> None:
|
||||
self.__internal_list += [item]
|
||||
self.__length += 1
|
||||
|
||||
@property
|
||||
def length(self) -> int:
|
||||
return self.__length
|
||||
|
||||
|
||||
@dataclass
|
||||
class GameObject:
|
||||
_position: InitVar[tuple[int, int]]
|
||||
|
||||
def __post_init__(self, position: tuple[int, int]) -> None:
|
||||
assert (0, 0) <= position
|
||||
self.__position = position
|
||||
|
||||
@property
|
||||
def position(self) -> tuple[int, int]:
|
||||
return self.__position
|
||||
|
||||
@position.setter
|
||||
def position(self, position: tuple[int, int]) -> None:
|
||||
if (0, 0) > position:
|
||||
return
|
||||
self.__position = position
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
xs: MyList[int] = MyList()
|
||||
xs.add(100)
|
||||
assert xs.length == 1
|
||||
position: tuple[int, int] = (0, 0)
|
||||
my_obj = GameObject(position)
|
||||
assert my_obj.position == (0, 0)
|
||||
try:
|
||||
GameObject((0, -1))
|
||||
except AssertionError:
|
||||
pass
|
||||
else:
|
||||
raise AssertionError(
|
||||
f"{my_obj} should have thrown a assertation error")
|
||||
my_obj.position = (-1, 0)
|
||||
assert my_obj.position == (0, 0)
|
@ -1,17 +0,0 @@
|
||||
---
|
||||
marp: true
|
||||
paginate: true
|
||||
class: invert
|
||||
# theme: uncover
|
||||
footer: Tutorium 10 - 22.12.2023 - Nils Pukropp - https://s.narl.io/s/tutorium-10
|
||||
header:
|
||||
---
|
||||
|
||||
# Tutorium 10 - 22.12.2023
|
||||
|
||||
Weihnachtsaufgabe :)
|
||||
|
||||
---
|
||||
|
||||
# Aufgabe - SpaceArena
|
||||
|
@ -1,22 +0,0 @@
|
||||
from result import Err, Ok, Panick, Result
|
||||
from ui import run_command, Color
|
||||
from spacearena import Difficulty, SpaceArena
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
difficulty: Difficulty | None = None
|
||||
while not difficulty:
|
||||
difficulty = Difficulty.get_difficulty(
|
||||
input(f"Choose your difficulty {[e.name.lower() for e in list(Difficulty)]}\n> ").upper())
|
||||
print(f"Difficulty: {difficulty} selected!")
|
||||
game = SpaceArena(difficulty)
|
||||
print("Starting game!")
|
||||
while game.is_running:
|
||||
Err("error value hehe").unwrap_or("yay still works")
|
||||
match run_command(input("> "), game):
|
||||
case Ok(value):
|
||||
print(f"{Color.OK}{value}{Color.ENDC}")
|
||||
case Err(value):
|
||||
print(f"{Color.BOLD}{Color.FAIL}Error:{Color.ENDC} {Color.WARNING}{value}{Color.ENDC}")
|
||||
|
||||
|
@ -1,92 +0,0 @@
|
||||
from abc import ABC
|
||||
from dataclasses import dataclass
|
||||
from typing import Callable, overload
|
||||
|
||||
class Panick(Exception):
|
||||
def __init__(self, msg_res: 'Err | str') -> None:
|
||||
match msg_res:
|
||||
case str(msg):
|
||||
super().__init__(msg)
|
||||
case _:
|
||||
super().__init__(f"thread `__main__` panicked:\ncalled `Result::unwrap()` on an `Err` value: \"{msg_res.value}\"")
|
||||
|
||||
|
||||
@dataclass
|
||||
class Result[V, E](ABC):
|
||||
def unwrap(self) -> V:
|
||||
match self:
|
||||
case Ok(value):
|
||||
return value
|
||||
raise Panick(self) # type: ignore
|
||||
|
||||
def expect(self, msg: str) -> V:
|
||||
match self:
|
||||
case Ok(value):
|
||||
return value
|
||||
raise Panick(msg)
|
||||
|
||||
def is_ok(self) -> bool:
|
||||
return isinstance(self, Ok)
|
||||
|
||||
def is_err(self) -> bool:
|
||||
return isinstance(self, Err)
|
||||
|
||||
def unwrap_or(self, value: V) -> V:
|
||||
match self:
|
||||
case Ok(val):
|
||||
return val
|
||||
return value
|
||||
|
||||
def and_[O](self, other: 'Result[O, E]') -> 'Result[O, E]':
|
||||
match self, other:
|
||||
case Err(value), _:
|
||||
return Err(value)
|
||||
return other
|
||||
|
||||
def and_then[O](self, func: Callable[[V], 'Result[O, E]']) -> 'Result[O, E]':
|
||||
match self:
|
||||
case Ok(value):
|
||||
return func(value)
|
||||
return Err(self.value) # type: ignore
|
||||
|
||||
@dataclass
|
||||
class Ok[V, E](Result[V, E]):
|
||||
value: V
|
||||
|
||||
@dataclass
|
||||
class Err[V, E](Result[V, E]):
|
||||
value: E
|
||||
|
||||
def __sqrt(num: float) -> Result[float, str]:
|
||||
if num < 0:
|
||||
return Err('negative sqrt')
|
||||
return Ok(num ** 0.5)
|
||||
|
||||
def test_result() -> None:
|
||||
assert Ok(4.0).and_then(__sqrt) == Ok(2.0)
|
||||
assert Ok(-5.0).and_then(__sqrt) == Err('negative sqrt')
|
||||
assert Err("haha error").and_then(__sqrt) == Err("haha error")
|
||||
assert Ok("test").unwrap() == "test"
|
||||
try:
|
||||
Err("haha error").unwrap()
|
||||
except Panick:
|
||||
pass
|
||||
else:
|
||||
assert False, 'didn\'t throw Panick'
|
||||
assert Err('haha error').unwrap_or("doch nicht") == 'doch nicht'
|
||||
|
||||
result = Ok('test result')
|
||||
|
||||
match result:
|
||||
case Ok(res):
|
||||
assert res == 'test result'
|
||||
case Err():
|
||||
pass
|
||||
|
||||
result = Err('test result')
|
||||
|
||||
match result:
|
||||
case Ok():
|
||||
pass
|
||||
case Err(res):
|
||||
assert res == 'test result'
|
@ -1,44 +0,0 @@
|
||||
from dataclasses import dataclass, InitVar
|
||||
from enum import Enum
|
||||
from typing import Optional
|
||||
|
||||
|
||||
class Difficulty(Enum):
|
||||
EASY = 0.5
|
||||
NORMAL = 1.0
|
||||
HARD = 2.0
|
||||
|
||||
@staticmethod
|
||||
def get_difficulty(input: str) -> Optional['Difficulty']:
|
||||
for e in list(Difficulty):
|
||||
if e.name == input:
|
||||
return e
|
||||
|
||||
def __str__(self):
|
||||
return self.name
|
||||
|
||||
|
||||
|
||||
@dataclass
|
||||
class SpaceArena:
|
||||
_difficulty: InitVar[Difficulty] = Difficulty.NORMAL
|
||||
|
||||
def __post_init__(self, difficulty: Difficulty = Difficulty.NORMAL) -> None:
|
||||
self.__difficulty = difficulty
|
||||
self.__is_running = True
|
||||
|
||||
|
||||
@property
|
||||
def difficulty(self) -> Difficulty:
|
||||
return self.__difficulty
|
||||
|
||||
@difficulty.setter
|
||||
def difficulty(self, new_diff: Difficulty):
|
||||
self.__difficulty = new_diff
|
||||
|
||||
@property
|
||||
def is_running(self) -> bool:
|
||||
return self.__is_running
|
||||
|
||||
def stop(self):
|
||||
self.__is_running = False
|
@ -1,79 +0,0 @@
|
||||
from abc import ABC, abstractmethod
|
||||
from enum import Enum
|
||||
import re
|
||||
from typing import override
|
||||
from result import Ok, Err, Result
|
||||
from spacearena import Difficulty, SpaceArena
|
||||
|
||||
class Color(Enum):
|
||||
OK = '\033[92m'
|
||||
FAIL = '\033[91m'
|
||||
WARNING = '\033[93m'
|
||||
ENDC = '\033[0m'
|
||||
BOLD = '\033[1m'
|
||||
UNDERLINE = '\033[4m'
|
||||
|
||||
def __str__(self) -> str:
|
||||
return self.value
|
||||
|
||||
|
||||
class AbstractCommand(ABC):
|
||||
|
||||
def matches(self, inp: str) -> Result[re.Match[str], None]:
|
||||
if m := re.match(self.pattern(), inp):
|
||||
return Ok(m)
|
||||
return Err(None)
|
||||
|
||||
@abstractmethod
|
||||
def pattern(self) -> str:
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
def run(self, game: SpaceArena, args: list[str]) -> Result[str, str]:
|
||||
pass
|
||||
|
||||
|
||||
class QuitCommand(AbstractCommand):
|
||||
@override
|
||||
def pattern(self) -> str:
|
||||
return r"(quit)"
|
||||
|
||||
@override
|
||||
def run(self, game: SpaceArena, _: list[str]) -> Result[str, str]:
|
||||
game.stop()
|
||||
return Ok('quitting game!')
|
||||
|
||||
class DifficultyCommand(AbstractCommand):
|
||||
@override
|
||||
def pattern(self) -> str:
|
||||
return r"(difficulty|diff)\s+(increase|decrease)"
|
||||
|
||||
@override
|
||||
def run(self, game: SpaceArena, args: list[str]) -> Result[str, str]:
|
||||
diffs = list(Difficulty)
|
||||
curr = diffs.index(game.difficulty)
|
||||
|
||||
match args[0]:
|
||||
case 'increase' if curr + 1 < len(diffs):
|
||||
game.difficulty = diffs[curr + 1]
|
||||
return Ok(f"Increasing difficulty to {game.difficulty}")
|
||||
case 'increase':
|
||||
return Err("maximum difficulty")
|
||||
case 'decrease' if curr - 1 >= 0:
|
||||
game.difficulty = diffs[curr - 1]
|
||||
return Ok(f"Decreasing difficulty to {game.difficulty}")
|
||||
case 'decrease':
|
||||
return Err("minimum difficulty")
|
||||
return Err("invalid input")
|
||||
|
||||
ALL_COMMANDS: list[AbstractCommand] = [QuitCommand(), DifficultyCommand()]
|
||||
|
||||
def run_command(inp: str, game: SpaceArena, commands: list[AbstractCommand] = ALL_COMMANDS) -> Result[str, str]:
|
||||
for command in commands:
|
||||
match command.matches(inp):
|
||||
case Ok(value):
|
||||
args = [str(s) for s in value.groups()]
|
||||
return command.run(game, args[1:])
|
||||
|
||||
|
||||
return Err("command not found")
|
@ -1,381 +0,0 @@
|
||||
---
|
||||
marp: true
|
||||
paginate: true
|
||||
class: invert
|
||||
# theme: uncover
|
||||
footer: Tutorium 11 - 12.01.2023 - Nils Pukropp - https://s.narl.io/s/tutorium-11
|
||||
header:
|
||||
---
|
||||
|
||||
# Tutorium 11
|
||||
|
||||
Dictionary, List-Comprehensions, OOP nochmal
|
||||
|
||||
---
|
||||
|
||||
## Dictionary
|
||||
|
||||
- Eine Ansammlung aus **Keys** und dessen **Werten**
|
||||
- Ordnet jedem **Key** einen **Wert** zu
|
||||
- Ein **Key** muss **immutable** sein, also keine `list`, `Objects`, ...
|
||||
- **Werte** können mutable sein, also eigentlich alles.
|
||||
|
||||
---
|
||||
|
||||
## Creating a Dictionary
|
||||
|
||||
```python
|
||||
dictionary = {
|
||||
<key>: <value>,
|
||||
<key>: <value>,
|
||||
...
|
||||
<key>: <value>
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Creating a Dictionary - Beispiel
|
||||
|
||||
- `Key`: Modul als `str` referenziert
|
||||
- **immutable**
|
||||
- `Value`: Liste aller Stundenten, mutable
|
||||
- **mutable**, wir können Stunden entfernen/hinzufügen
|
||||
|
||||
```python
|
||||
courses: dict[str, list[str]] = {
|
||||
"eidp": ["np19", "az34", "jf334"],
|
||||
"mathe": ["aw331", "pl67"],
|
||||
"sdp": ["xy111", "xz112"],
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Was passiert wenn wir eine `list` als nehmen?
|
||||
|
||||
- `list[Any]` ist mutable, genauer nicht **hashable**
|
||||
- `hash([1, 2, 3])` wirft einen Error
|
||||
- `dict` nutzt `hash(...)` für lookups
|
||||
- `dict[list[Any], Any]` wirft also einen `TypeError`, weil `list` nicht **hashable** ist
|
||||
- `tuple` sind immutable, wenn dessen Elemente immutable sind
|
||||
- z.B. `(1, 2)`
|
||||
|
||||
---
|
||||
|
||||
## Dictionary indizieren
|
||||
|
||||
```python
|
||||
print(courses["eidp"]) # ["np19", "az34", "jf334"]
|
||||
|
||||
courses["eidp"] += ["jk331"]
|
||||
|
||||
print(courses["eidp"]) # ["np19", "az34", "jf334"]
|
||||
|
||||
courses["mathe_2"] += ["jk331"] # KeyError
|
||||
|
||||
courses["mathe_2"] = ["jk331"] # fügt "mathe_2" hinzu mit dem Wert ["jk331"]
|
||||
|
||||
if "logik" not in courses:
|
||||
print("logik is not in courses!")
|
||||
courses["logik"] = []
|
||||
print("now it is!")
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Was kann man als Value verwenden?
|
||||
|
||||
---
|
||||
|
||||
## Was kann man als Value verwenden?
|
||||
|
||||
**Alles**!
|
||||
|
||||
```python
|
||||
ops: dict[str, Callable] = {
|
||||
'+': lambda x, y: x + y,
|
||||
'-': lambda x, y: x - y,
|
||||
'*': lambda x, y: x * y,
|
||||
'/': lambda x, y: x / y,
|
||||
}
|
||||
|
||||
print(ops['+'](3, 1)) # 4
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Dictionary iterieren
|
||||
|
||||
- mit der `items()` Methode bekommt man jeden `Key` mit `Value`
|
||||
|
||||
```python
|
||||
for courses, students in courses.items():
|
||||
print(f"{courses}: {students}")
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## List-Comprehension
|
||||
|
||||
- hattet ihr noch nicht in der Vorlesung
|
||||
- Viel zu Viele nutzen es schon, und ich will keine 0 Punkte geben
|
||||
- Syntax-Sugar für das erstellen von Listen basierend auf anderen Listen
|
||||
|
||||
```python
|
||||
even_numbers = [number for number in range(101) if number % 2 == 0] # [0, 2, ..., 100]
|
||||
|
||||
# [(0, 0), (0, 1), (0, 2), (1, 0), (1, 1), (1, 2), (2, 0), (2, 1), (2, 2)]
|
||||
all_permutations = [(a, b) for a in range(3)
|
||||
for b in range(3)]
|
||||
|
||||
# quiet fast actually (for python)
|
||||
pythagorean_triples = [(a, b, c) for a in range(101)
|
||||
for b in range(101)
|
||||
for c in range(101)
|
||||
if a ** 2 + b ** 2 == c ** 2]
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## List-Comprehension
|
||||
|
||||
```python
|
||||
# ew no syntax sugar
|
||||
all_students: set[str] = set()
|
||||
for _, students in courses.items():
|
||||
for student in students:
|
||||
all_students.add(student)
|
||||
|
||||
# syntax sugar!
|
||||
print({student for students in courses.values()
|
||||
for student in students})
|
||||
|
||||
# flattening stuff
|
||||
matrix = [[1, 0, 0], [0, 1, 0], [0, 0, 1]]
|
||||
print([num for row in matrix for num in row]) # [1, 0, 0, 0, 1, 0, 0, 0, 1]
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Übertreibt es aber nicht!
|
||||
|
||||
- List-Comprehensions sind zum *erstellen* von Listen.
|
||||
- List-Comprehensions sollten **nichts** *machen*
|
||||
|
||||
Also **kein** side effects oder Funktionsaufrufe!
|
||||
|
||||
```python
|
||||
x = [1, 2, 3, 4]
|
||||
[x.append(num) for num in range(5, 11)] # really bad
|
||||
```
|
||||
|
||||
```python
|
||||
def f(x: float) -> float:
|
||||
return x ** 2 + 3 * x + 1
|
||||
|
||||
[f(x) for x in range(101)] # bad
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## OOP - Funktion oder Methode?
|
||||
|
||||
```python
|
||||
import math
|
||||
from dataclasses import dataclass
|
||||
|
||||
|
||||
@dataclass
|
||||
class Position:
|
||||
x: float
|
||||
y: float
|
||||
|
||||
def distance(self, other: Position) -> float:
|
||||
return math.sqrt((other.x - self.x) ** 2 + (other.y - self.y) ** 2)
|
||||
|
||||
def distance_of(a: Position, b: Position) -> float:
|
||||
return math.sqrt((b.x - a.x) ** 2 + (b.y - a.y) ** 2)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## OOP - Funktion oder Methode!
|
||||
|
||||
- `distance(self, other: Position)` ist eine Methode.
|
||||
- Gehört zu einer Klasse und hat `self` als Parameter
|
||||
- `distance_of(a: Position, b: Position)` ist eine Funktion.
|
||||
- unabhängig (normalerweise kein **state**)
|
||||
|
||||
---
|
||||
|
||||
## Was ist ein **State** (Zustand)?
|
||||
|
||||
```python
|
||||
class GameState(Enum):
|
||||
RUNNING = auto()
|
||||
PAUSED = auto()
|
||||
ENDED = auto()
|
||||
|
||||
@dataclass
|
||||
class Game:
|
||||
state: GameState
|
||||
```
|
||||
|
||||
- Unser `Game`-Objekt hat einen Zustand der sich ändern kann
|
||||
- Unser `Game` kann pausiert, beendet oder am Laufen sein
|
||||
- Dieser Zustand kann sich ändern
|
||||
|
||||
---
|
||||
|
||||
## Was ist ein **State** (Zustand)?
|
||||
|
||||
```python
|
||||
@dataclass
|
||||
class Position:
|
||||
x: float
|
||||
y: float
|
||||
```
|
||||
|
||||
Ebenso sind `x` und `y` Zustände von `Position`, wenn auch nicht ganz so offensichtlich.
|
||||
|
||||
- Beschreiben das Objekt
|
||||
- Können sich ändern
|
||||
|
||||
---
|
||||
|
||||
## Was ist ein **State** (Zustand)?
|
||||
|
||||
Wie sieht es mit `distance_of(...)` aus?
|
||||
|
||||
```python
|
||||
def distance_of(a: Position, b: Position) -> float:
|
||||
return math.sqrt((b.x - a.x) ** 2 + (b.y - a.y) ** 2)
|
||||
```
|
||||
|
||||
- Verhält sich immer gleich
|
||||
- also hat keinen **State**
|
||||
- ändert keine **States**
|
||||
- manchmal passiert das leider, ist aber ein schlechter Stil!
|
||||
```python
|
||||
def move_to(pos: Position, x: float, y: float):
|
||||
pos.x = x
|
||||
pos.y = y
|
||||
```
|
||||
- Guter Stil ist es eigentlich immer die Parameter in Ruhe zu lassen!
|
||||
|
||||
---
|
||||
|
||||
## Funktionen mit **State**
|
||||
|
||||
- Ihr kriegt 0 Punkte für die gesamte Abgabe.
|
||||
|
||||
```python
|
||||
can_execute = True
|
||||
|
||||
def function(x: int) -> int:
|
||||
global can_execute
|
||||
if can_execute:
|
||||
can_execute = False
|
||||
return x + 1
|
||||
return x
|
||||
|
||||
def can_execute_again():
|
||||
global can_execute
|
||||
can_execute = True
|
||||
```
|
||||
|
||||
- Ich meine das ernst mit den 0 Punkten
|
||||
|
||||
---
|
||||
|
||||
## Dazu zählt auch sowas!
|
||||
|
||||
```python
|
||||
def main():
|
||||
session = Session()
|
||||
|
||||
def do_something():
|
||||
session.do()
|
||||
# ...
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Private, Getter, Setter
|
||||
|
||||
Bei `@dataclass`:
|
||||
- `InitVar` verwenden wenn im Klassenrumpf deklariert
|
||||
- Sollen mit `_<variable_name>` benannt werden
|
||||
- `__post_init__(self, <variable>)` muss definiert werden und `__<variable_name>` erstellen!
|
||||
|
||||
```python
|
||||
@dataclass
|
||||
class Point:
|
||||
_x: InitVar[float]
|
||||
_y: InitVar[float]
|
||||
|
||||
def __post_init__(self, x, y):
|
||||
self.__x = x
|
||||
self.__y = y
|
||||
```
|
||||
---
|
||||
|
||||
## Private, Getter, Setter
|
||||
|
||||
- Geht auch ohne `InitVar`
|
||||
- Keine Parameter für `__post_init__`
|
||||
- Also auch keine Parameter beim erstellen
|
||||
```python
|
||||
@dataclass
|
||||
class Point:
|
||||
def __post_init__(self):
|
||||
self.__x = 0
|
||||
self.__y = 0
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Private, Getter, Setter
|
||||
|
||||
- `x` und `y` sind nicht mehr von außen sichtbar
|
||||
```python
|
||||
p = Point()
|
||||
print(p.__x) # Error
|
||||
```
|
||||
- außer man erstellt einen *Getter* (`@property`)
|
||||
```python
|
||||
@dataclass
|
||||
class Point:
|
||||
# ...
|
||||
|
||||
@property
|
||||
def x(self) -> float:
|
||||
return self.__x
|
||||
|
||||
print(Point(3, 1).x) # Prints 3
|
||||
```
|
||||
---
|
||||
|
||||
## Private, Getter, Setter
|
||||
|
||||
Genauso kann man auch private Attribute setzbar machen:
|
||||
|
||||
```python
|
||||
@dataclass
|
||||
class Point:
|
||||
# ...
|
||||
|
||||
@x.setter
|
||||
def x(self, new_value: float):
|
||||
self.__x = new_value
|
||||
|
||||
p = Point(3, 1)
|
||||
print(p.x) # 3
|
||||
p.x = 4
|
||||
print(p.x) # 4
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
# Fragen?
|
@ -1,51 +0,0 @@
|
||||
from dataclasses import InitVar, dataclass
|
||||
from typing import Any, Callable
|
||||
|
||||
|
||||
def iterating_dict():
|
||||
courses: dict[str, list[str]] = {
|
||||
"eidp": ["np19", "az34", "jf334"],
|
||||
"mathe": ["aw331", "pl67"],
|
||||
"sdp": ["xy111", "xz112"],
|
||||
}
|
||||
|
||||
for course, students in courses.items():
|
||||
print(f"{courses}: {students}")
|
||||
|
||||
def mutable_keys():
|
||||
lists_as_keys: dict[list[str], str] = {
|
||||
["np19", "az34", "jf334"]: "eidp", # type: ignore
|
||||
["aw331", "pl67"]: "mathe", # type: ignore
|
||||
["xy111", "xz112"]: "sdp", # type: ignore
|
||||
}
|
||||
print(lists_as_keys)
|
||||
|
||||
def what_is_a_value():
|
||||
ops: dict[str, Callable] = {
|
||||
'+': lambda x, y: x + y,
|
||||
'-': lambda x, y: x - y,
|
||||
'*': lambda x, y: x * y,
|
||||
'/': lambda x, y: x / y,
|
||||
}
|
||||
|
||||
can_execute = True
|
||||
def function(x: int) -> int:
|
||||
global can_execute
|
||||
if can_execute:
|
||||
can_execute = False
|
||||
return x + 1
|
||||
return x
|
||||
|
||||
def can_execute_again():
|
||||
global can_execute
|
||||
can_execute = True
|
||||
|
||||
@dataclass
|
||||
class Point:
|
||||
def __post_init__(self):
|
||||
self.__x = 0
|
||||
self.__y = 0
|
||||
|
||||
@property
|
||||
def x(self) -> float:
|
||||
return self.__x
|
@ -1,424 +0,0 @@
|
||||
---
|
||||
marp: true
|
||||
paginate: true
|
||||
# class: invert
|
||||
theme: rose-pine
|
||||
footer: Tutorium 13 - 26.01.2024 - Nils Pukropp - https://s.narl.io/s/tutorium-13
|
||||
header:
|
||||
math: mathjax
|
||||
---
|
||||
|
||||
# Tutorium 13 - 26.01.2024
|
||||
|
||||
Orga - Wiederholung Types - Functions! - Decorator
|
||||
|
||||
---
|
||||
|
||||
# Orga
|
||||
|
||||
---
|
||||
## Wegen fehlendem Tutorium am 19.01.
|
||||
|
||||
- Jeder kriegt die 6 Punkte für Anwesenheit
|
||||
- Auf Blatt 13 als Extrapunkte unter *Anmerkungen*
|
||||
- Sorry fürs nicht beantworten von manchen Nachrichten
|
||||
- Falls ihr glaubt ihr bekommt knapp nicht genug Punkte schreibt mich einfach an, man wird schon noch irgendwo Punkte finden
|
||||
|
||||
---
|
||||
|
||||
## Syntax-Fehler
|
||||
|
||||
- Für **Syntax-Fehler** habe ich im allgemeinen **0 Punkte** in der jeweiligen Datei vergeben
|
||||
- Das euer Programm ausführbar ist sollte das mindeste sein!
|
||||
- Ihr sollt euer Programm sowieso selbständig testen und ich geh mal davon aus das ist nicht passiert wenn sich die Datei nichtmal ausführen lässt
|
||||
- Zeitdruck kann ich voll nachvollziehen
|
||||
|
||||
---
|
||||
|
||||
## Nachträgliches ausbessern
|
||||
|
||||
- Ihr verbessert euren SyntaxFehler (eure Python-Datei ist ausführbar)
|
||||
- Ihr schickt mir eine `.zip` oder eine `.tar.gz` mit dem verbesserten Code an [nils@narl.io](mailto:nils@narl.io)
|
||||
- verbessert nichts anderes!
|
||||
- Schreibt kurz in die Mail welches Blatt + Aufgabe + Kürzel
|
||||
- Ich korrigiere eure Abgabe nachträglich und ihr bekommt zumindest mehr als 0 Punkte
|
||||
- Bitte nur wenn ihr wirklich die Punkte braucht und habt etwas Geduld mit der Korrektur
|
||||
|
||||
---
|
||||
|
||||
## Allgemeines
|
||||
|
||||
- biete euch Übungen passend zur Klausur
|
||||
- kein genaues Datum, aber vor dem 09.02
|
||||
- Klausur ist *wahrscheinlich* am 19.02.
|
||||
- Short-Link zu der Übung [https://s.narl.io/s/eidp-ub](https://s.narl.io/s/eidp-ub)
|
||||
- aktuell noch nicht online
|
||||
|
||||
---
|
||||
|
||||
# Link: [https://s.narl.io/s/eidp-ub](https://s.narl.io/s/eidp-ub)
|
||||
|
||||
---
|
||||
|
||||
# Type annotations
|
||||
(Wiederholung)
|
||||
|
||||
---
|
||||
|
||||
## Type annotations - Was ist das?
|
||||
|
||||
---
|
||||
|
||||
## Type annotations - Was ist das?
|
||||
|
||||
* Jedes **Objekt** lässt sich mindestens einem **Typ** zuordnen
|
||||
* Objekte im mathematischen Sinne wie z.B. Variablen, Funktionen, ...
|
||||
* Dieser **schränkt** den Wertebereich ein
|
||||
* z.B. ist eine Variable `x` von Typ `int` eine Ganzzahl
|
||||
* ähnlich zur mathematischen Schreibweise $x \in \mathbb{Z}$
|
||||
* In der Informatik nennt man das **Typisierung**
|
||||
* Es gibt verschiedene Arten der Typisierung
|
||||
|
||||
---
|
||||
|
||||
## Type annotations - Typisierung
|
||||
|
||||
- **dynamische Typisierung** überprüft die gegebenen Typen zur **Laufzeit**
|
||||
- also erst wenn das Programm *läuft*
|
||||
- **statische Typisierung** überprüft die gegebenen Typen zur **Übersetzungszeit**
|
||||
- also während wir den Quellcode übersetzen
|
||||
|
||||
---
|
||||
|
||||
## Type annotations - Typisierung
|
||||
|
||||
- **dynamische Typisierung** überprüft die gegebenen Typen zur **Laufzeit**
|
||||
- also erst wenn das Programm *läuft*
|
||||
- **statische Typisierung** überprüft die gegebenen Typen zur **Übersetzungszeit**
|
||||
- also während wir den Quellcode übersetzen
|
||||
|
||||
### Was ist nun Python?
|
||||
|
||||
---
|
||||
|
||||
### Was ist nun Python?
|
||||
|
||||
- **dynamisch typisiert**
|
||||
- wir müssen unsere `.py` Datei ausführen bevor wir wissen ob alles korrekt ist
|
||||
- **Pylance** ist ein eigenes Programm
|
||||
- es soll beim Schreiben bereits **Typverletzungen** erkennen
|
||||
- **unvollständige** Typüberprüfung, es soll nur den Entwickler unterstützen
|
||||
|
||||
---
|
||||
|
||||
## Variabeln Typannotieren
|
||||
|
||||
* `variable_name: <Type> = ...`
|
||||
* Beispiele:
|
||||
```python
|
||||
x: int = 3
|
||||
y: int = 5
|
||||
string: str = "Hello World!"
|
||||
|
||||
# aber auch eigene Objekte (OOP)
|
||||
point: Point = Point(3, 1)
|
||||
```
|
||||
* diese Annotation ist für uns **optional**
|
||||
|
||||
---
|
||||
|
||||
## Funktionen Typannotieren
|
||||
|
||||
* `def func_name(param1: <Type>, param2: <Type>, ...) -> <Type>`
|
||||
* Beispiele:
|
||||
```python
|
||||
def add(x: int, y: int) -> int:
|
||||
return x + y
|
||||
|
||||
def div(x: float, y: float) -> Optional[float]:
|
||||
if y == 0.0:
|
||||
return None
|
||||
return x / y
|
||||
```
|
||||
* diese Annotation ist **verpflichtend** und muss so vollständig wie möglich sein
|
||||
|
||||
---
|
||||
|
||||
## Klassen Typannotieren
|
||||
|
||||
*
|
||||
```
|
||||
class ClassName:
|
||||
attribute_name1: <Type>
|
||||
attribute_name2: <Type>
|
||||
...
|
||||
```
|
||||
* Beispiel:
|
||||
```python
|
||||
@dataclass
|
||||
class Point:
|
||||
x: int
|
||||
y: int
|
||||
```
|
||||
* diese Annotation ist **verpflichtend** und muss so vollständig wie möglich sein
|
||||
|
||||
---
|
||||
|
||||
## Methoden Typannotieren
|
||||
|
||||
* `def method_name(self, param1: <Type>, ...) -> <Type>`
|
||||
* Beispiel:
|
||||
```python
|
||||
class Point:
|
||||
x: int
|
||||
y: int
|
||||
|
||||
def distance_from(self, other: 'Point') -> float:
|
||||
return math.sqrt((other.x - self.x) ** 2 + (other.y - self.y) ** 2)
|
||||
```
|
||||
* `self` muss **nicht** Typannotiert werden, kann aber
|
||||
* `other` hingegen schon, wegen Python muss in der Klasse mit `'` annotiert werden
|
||||
* diese Annotation ist **verpflichtend**
|
||||
|
||||
---
|
||||
|
||||
## Datentypen von Datentypen
|
||||
|
||||
* Manche Datentypen bauen sich aus anderen Datentypen auf
|
||||
* z.B. `list` ist eine Liste von Elementen mit einem Typ
|
||||
* hierfür verwenden wir `[]` um den Datentyp in `list` zu annotieren
|
||||
```python
|
||||
def sum(xs: list[int]) -> int:
|
||||
total: int = 0
|
||||
for x in xs:
|
||||
total += x
|
||||
return total
|
||||
```
|
||||
* hierbei ist es wichtig so genau wie möglich zu annotieren!
|
||||
* diese Annotation ist **verpflichtend**
|
||||
|
||||
---
|
||||
|
||||
## Häufige Fehler mit verschachtelten Typen
|
||||
|
||||
---
|
||||
|
||||
## Fehlerquelle - `tuple[...]`
|
||||
|
||||
* Tuple haben eine feste größe
|
||||
* Tuple sind endlich
|
||||
* Tuple können Elemente mit unterschiedlichen Typen haben
|
||||
* Die Datentypen der Elemente werden mit einem `,` in `[]` getrennt
|
||||
* Beispiel:
|
||||
```python
|
||||
tup: tuple[int, int, float, str] = (1, 2, 3.0, "hello world")
|
||||
```
|
||||
* Diese Annotation ist **verpflichtend**
|
||||
|
||||
---
|
||||
|
||||
## Fehlerquelle - `dict[...]`
|
||||
|
||||
* Dictionary haben genau zwei zu definierende Typen
|
||||
* **Key**
|
||||
* **Value**
|
||||
* Beispiel:
|
||||
```python
|
||||
number_dictionary: dict[int, str] = {
|
||||
0: "zero",
|
||||
1: "one",
|
||||
2: "two",
|
||||
}
|
||||
```
|
||||
* Diese Annotation ist **verpflichtend**
|
||||
* Diese kann weiter geschachtelt werden durch z.B. `list` als `Value`:
|
||||
* `dict[int, list[str]]`
|
||||
|
||||
---
|
||||
|
||||
## Fehlerquelle - Typvariabeln (generische Typen)
|
||||
|
||||
* manchmal wollen wir nicht genau wissen welchen Datentypen wir haben
|
||||
* dieser wird dann implizit von Python erkannt
|
||||
* wir stellen damit sicher dass eine Typvariable **beliebig** aber **fest** ist
|
||||
* Beispiel:
|
||||
```python
|
||||
def add[T](x: T, y: T) -> T:
|
||||
return x + y
|
||||
```
|
||||
* `T` kann nur ein Datentyp sein, also muss `type(x) == type(y)` gelten
|
||||
* **außer** wir schrenken `T` mit `|` ein: `T: (int | str)` damit müssen x und y nicht den gleichen Datentypen haben
|
||||
* `T` lässt sich weiter einschränken durch `T: (int, str)`, hierbei ist `T` entweder ein `int` oder (exklusiv) `str`
|
||||
|
||||
---
|
||||
|
||||
## Fehlerquelle - Was ist TypeVar?
|
||||
|
||||
* `TypeVar` ist aus früheren Python-Versionen
|
||||
* Typvariablen wurden vor der Python 3.12 so definiert:
|
||||
```python
|
||||
T = TypeVar('T')
|
||||
```
|
||||
* sieht dumm aus, ist es auch, benutzt es nicht!
|
||||
|
||||
---
|
||||
|
||||
## Fragen zu Typannotationen?
|
||||
|
||||
---
|
||||
|
||||
# Funktionale Programmierung
|
||||
|
||||
---
|
||||
|
||||
## Funktionale Programmierung - was ist das?
|
||||
|
||||
- Funktionen sind äquivalent zu Datenobjekten
|
||||
- anonyme Funktionen aka Lambdas
|
||||
- Closures
|
||||
- Programmablauf mit Verkettung und Komposition von Funktionen
|
||||
|
||||
---
|
||||
|
||||
## Funktionen sind Datenobjekte
|
||||
|
||||
- Jede Funktion hat den Datentyp `Callable`
|
||||
- Wir können Funktionen wie alle anderen Objekte variabeln zuweisen
|
||||
```python
|
||||
def add(a: int, b: int) -> int:
|
||||
return a + b
|
||||
|
||||
add_but_variable = add
|
||||
|
||||
print(add_but_variable(3, 2)) # 5
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Anonyme Funktionen - `lambda`
|
||||
|
||||
- Mit dem `lambda` Keyword lassen sich anonyme Funktionen definieren ohne `def`
|
||||
- Bietet sich vor allem an für kleine Funktionen und Kompositionen von Funktionen
|
||||
```python
|
||||
print(reduce(lambda x, y: x + y, [1, 2, 3, 4])) # 10
|
||||
```
|
||||
- hat als Datentyp auch `Callable`
|
||||
```python
|
||||
add: Callable[[int, int], int] = lambda x, y: x + y
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Closures
|
||||
|
||||
- Verkettete Funktionen, bei denen die Variabeln aus vorherigen benutzt werden können
|
||||
```python
|
||||
def poly(x: float) -> Callable[[float, float], Callable[[float], float]]:
|
||||
return lambda a, b: lambda c: a * x ** 2 + b * x + c
|
||||
|
||||
print(poly(3)(2, 3)(5)) # 2 * 3 ** 2 + 3 * 3 + 5 = 32
|
||||
```
|
||||
- kein wirklich schönes Beispiel, ein besseres ist `compose` für Kompositionen
|
||||
|
||||
---
|
||||
|
||||
## Komposition
|
||||
|
||||
- Verketten von Funktionen
|
||||
```python
|
||||
def compose[T](*funcs: Callable[[T], T]) -> Callable[[T], T]:
|
||||
return fold(lambda f, g: lambda n: f(g(n)), funcs)
|
||||
|
||||
f: Callable[[int], int] = lambda n: n + 42
|
||||
g: Callable[[int], int] = lambda n: n ** 2
|
||||
h: Callable[[int], int] = lambda n: n - 3
|
||||
|
||||
print(compose(f, g, h)(0))
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Higher-Order Functions
|
||||
|
||||
- nehmen eine oder mehrere `Callable` als Argument
|
||||
- geben ein `Callable` zurück
|
||||
|
||||
### Higher-Order-Function - `map`
|
||||
|
||||
- Wendet ein `Callable` auf jedes Element in einem `Iterable` an
|
||||
|
||||
```python
|
||||
def map[T, R](func: Callable[[T], R], xs: Iterable[T]) -> Iterable[R]:
|
||||
return [func(x) for x in xs]
|
||||
|
||||
numeric_list = list(map(lambda e: int(e), ['1', '2', '3']))
|
||||
print(numeric_list) # [1, 2, 3]
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Higher-Order-Function - `filter`
|
||||
|
||||
- `filter` verarbeitet Datenstrukturen anhand eines Prädikats (`Callable`)
|
||||
- behält nur Elemente die das Prädikat erfüllen
|
||||
```python
|
||||
def filter[T](predicate: Callable[[T], bool], xs: Iterable[T]) -> Iterable[T]:
|
||||
return [x for x in xs if predicate(x)]
|
||||
|
||||
predicate: Callable[[int | None] bool] = lambda e: e is not None
|
||||
none_free_list: list[int] = list(filter(predicate, [1, 2, 3, None, 5, 6]))
|
||||
print(none_free_list) # [1, 2, 3, 5, 6] - kein None
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Higher-Order-Function - `fold`
|
||||
|
||||
- Kombiniert Elemente einer Datenstruktur
|
||||
```python
|
||||
def fold[T](func: Callable[[T, T], T], xs: Iterable[T]) -> T:
|
||||
it: Iterator[T] = iter(xs)
|
||||
value: T | None = None
|
||||
for x in it:
|
||||
match value:
|
||||
case None:
|
||||
value = x
|
||||
case _:
|
||||
value = func(value, x)
|
||||
if not value:
|
||||
raise TypeError("can't fold empty list")
|
||||
return value
|
||||
|
||||
sum: Callable[[Iterable[int]], int] = lambda xs: fold(lambda x, y: x + y, xs)
|
||||
print(sum([1, 2, 3, 4])) # 10
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### keine Higher-Order-Function - `flatten`
|
||||
|
||||
- Nimmt mehrdimensionale Listen und macht eine Liste draus
|
||||
```python
|
||||
def flatten(xs: Iterable[Any]) -> Iterable[Any]:
|
||||
new_list = []
|
||||
for s in xs:
|
||||
if isinstance(s, Iterable):
|
||||
new_list += flatten(s)
|
||||
else:
|
||||
new_list.append(s)
|
||||
return new_list
|
||||
|
||||
flattened = list(flatten([[1, 2, 3], 4, [[5, 6], 7, [8, 9]]]))
|
||||
print(flattened) # [1, 2, 3, 4, 5, 6, 7, 8, 9]
|
||||
```
|
||||
- nimmt weder `Callable` als Argumente
|
||||
- gibt kein `Callable` zurück
|
||||
- ist keine Higher-Order-Function
|
||||
|
||||
---
|
||||
|
||||
# Fragen zur funktionalen Programmierung?
|
||||
|
||||
---
|
||||
|
||||
# Weitere allgemeine Fragen?
|
@ -1,21 +0,0 @@
|
||||
# Notes for Tutorium 12 - 19.01.2024
|
||||
|
||||
## Topics
|
||||
|
||||
- Type Annotation - The Full Guide
|
||||
- Basics
|
||||
- parameter
|
||||
- variable
|
||||
- return type
|
||||
-
|
||||
- Advanced
|
||||
- Collections
|
||||
- ~~args, kwargs~~
|
||||
- Functional programming
|
||||
- `lambda`
|
||||
- map
|
||||
- filter
|
||||
- reduce
|
||||
- flatten
|
||||
- function composition
|
||||
- Maybe-Type
|
@ -1,84 +0,0 @@
|
||||
from typing import Any, Callable, Iterable, Iterator
|
||||
|
||||
|
||||
def map[T, R](func: Callable[[T], R], xs: Iterable[T]) -> Iterable[R]:
|
||||
return [func(x) for x in xs]
|
||||
|
||||
|
||||
def filter[T](predicate: Callable[[T], bool], xs: Iterable[T]) -> Iterable[T]:
|
||||
return [x for x in xs if predicate(x)]
|
||||
|
||||
|
||||
def fold[T](func: Callable[[T, T], T], xs: Iterable[T]) -> T:
|
||||
it: Iterator[T] = iter(xs)
|
||||
value: T | None = None
|
||||
for x in it:
|
||||
match value:
|
||||
case None:
|
||||
value = x
|
||||
case _:
|
||||
value = func(value, x)
|
||||
if not value:
|
||||
raise TypeError("can't fold empty list")
|
||||
return value
|
||||
|
||||
def flatten(xs: Iterable[Any]) -> Iterable[Any]:
|
||||
new_list = []
|
||||
for s in xs:
|
||||
if isinstance(s, Iterable):
|
||||
new_list += flatten(s)
|
||||
else:
|
||||
new_list.append(s)
|
||||
return new_list
|
||||
|
||||
def compose[T](*funcs: Callable[[T], T]) -> Callable[[T], T]:
|
||||
return fold(lambda f, g: lambda n: f(g(n)), funcs)
|
||||
|
||||
|
||||
def poly(x: float) -> Callable[[float, float], Callable[[float], float]]:
|
||||
return lambda a, b: lambda c: a * x ** 2 + b * x + c
|
||||
|
||||
def main():
|
||||
f: Callable[[int], int] = lambda n: n + 42
|
||||
g: Callable[[int], int] = lambda n: n ** 2
|
||||
h: Callable[[int], int] = lambda n: n - 3
|
||||
|
||||
fhg: Callable[[int], int] = compose(f, g, h)
|
||||
|
||||
# f(g(h(0))) <=> ((0 - 3) ** 2) + 42 = 51
|
||||
assert (tmp := fhg(0)) == 51
|
||||
assert compose(f, g, h)(0) == 51
|
||||
predicate = lambda e: e
|
||||
assert list(filter(predicate, [1, 2, 3, None, 5, 6])) == [1, 2, 3, 5, 6]
|
||||
assert list(filter(lambda e: e is None, [1, 2, 3, None, 5, 6])) == [None]
|
||||
|
||||
assert list(map(lambda e: str(e), [1, 2, 3, 4, 5, 6, "hello_functional"])) == ["1", "2", "3", "4", "5", "6", "hello_functional"]
|
||||
|
||||
assert list(
|
||||
filter(lambda e: len(e) > 1,
|
||||
map(lambda e: str(e),
|
||||
[1, 2, 3, 4, "hello_world"]))) == ["hello_world"]
|
||||
|
||||
assert list(filter(lambda e: isinstance(e, int), [1, 2, 3, "hello"])) == [1, 2, 3]
|
||||
assert (tmp := list(flatten([[1, 2, 3], 4, [[5, 6], 7, [8, 9]]]))) == [1, 2, 3, 4, 5, 6, 7, 8, 9], f"{tmp}"
|
||||
|
||||
def add(a: int, b: int) -> int:
|
||||
return a + b
|
||||
|
||||
add_but_variable: Callable[[int, int], int] = add
|
||||
|
||||
assert add_but_variable(3, 2) == 5
|
||||
|
||||
add2: Callable[[int, int], int] = lambda x, y: x + y
|
||||
|
||||
assert add2(2, 3) == 5
|
||||
|
||||
assert (lambda x, y: x + y)(3, 4) == 7
|
||||
|
||||
sum: Callable[[Iterable[int]], int] = lambda xs: fold(lambda x, y: x + y, xs)
|
||||
assert sum([1, 2, 3, 4]) == 10
|
||||
|
||||
assert poly(3)(2, 3)(5) == 2 * 3 ** 2 + 3 * 3 + 5
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
@ -1,424 +0,0 @@
|
||||
---
|
||||
marp: true
|
||||
paginate: true
|
||||
# class: invert
|
||||
theme: rose-pine
|
||||
footer: Tutorium 13 - 26.01.2024 - Nils Pukropp - https://s.narl.io/s/tutorium-13
|
||||
header:
|
||||
math: mathjax
|
||||
---
|
||||
|
||||
# Tutorium 13 - 26.01.2024
|
||||
|
||||
Orga - Wiederholung Types - Functions!
|
||||
|
||||
---
|
||||
|
||||
# Orga
|
||||
|
||||
---
|
||||
## Wegen fehlendem Tutorium am 19.01.
|
||||
|
||||
- Jeder kriegt die 6 Punkte für Anwesenheit
|
||||
- Auf Blatt 13 als Extrapunkte unter *Anmerkungen*
|
||||
- Sorry fürs nicht beantworten von manchen Nachrichten
|
||||
- Falls ihr glaubt ihr bekommt knapp nicht genug Punkte schreibt mich einfach an, man wird schon noch irgendwo Punkte finden
|
||||
|
||||
---
|
||||
|
||||
## Syntax-Fehler
|
||||
|
||||
- Für **Syntax-Fehler** habe ich im allgemeinen **0 Punkte** in der jeweiligen Datei vergeben
|
||||
- Das euer Programm ausführbar ist sollte das mindeste sein!
|
||||
- Ihr sollt euer Programm sowieso selbständig testen und ich geh mal davon aus das ist nicht passiert wenn sich die Datei nichtmal ausführen lässt
|
||||
- Zeitdruck kann ich voll nachvollziehen
|
||||
|
||||
---
|
||||
|
||||
## Nachträgliches ausbessern
|
||||
|
||||
- Ihr verbessert euren SyntaxFehler (eure Python-Datei ist ausführbar)
|
||||
- Ihr schickt mir eine `.zip` oder eine `.tar.gz` mit dem verbesserten Code an [nils@narl.io](mailto:nils@narl.io)
|
||||
- verbessert nichts anderes!
|
||||
- Schreibt kurz in die Mail welches Blatt + Aufgabe + Kürzel
|
||||
- Ich korrigiere eure Abgabe nachträglich und ihr bekommt zumindest mehr als 0 Punkte
|
||||
- Bitte nur wenn ihr wirklich die Punkte braucht und habt etwas Geduld mit der Korrektur
|
||||
|
||||
---
|
||||
|
||||
## Allgemeines
|
||||
|
||||
- biete euch Übungen passend zur Klausur
|
||||
- kein genaues Datum, aber vor dem 09.02
|
||||
- Klausur ist *wahrscheinlich* am 19.02.
|
||||
- Short-Link zu der Übung [https://s.narl.io/s/eidp-ub](https://s.narl.io/s/eidp-ub)
|
||||
- aktuell noch nicht online
|
||||
|
||||
---
|
||||
|
||||
# Link: [https://s.narl.io/s/eidp-ub](https://s.narl.io/s/eidp-ub)
|
||||
|
||||
---
|
||||
|
||||
# Type annotations
|
||||
(Wiederholung)
|
||||
|
||||
---
|
||||
|
||||
## Type annotations - Was ist das?
|
||||
|
||||
---
|
||||
|
||||
## Type annotations - Was ist das?
|
||||
|
||||
* Jedes **Objekt** lässt sich mindestens einem **Typ** zuordnen
|
||||
* Objekte im mathematischen Sinne wie z.B. Variablen, Funktionen, ...
|
||||
* Dieser **schränkt** den Wertebereich ein
|
||||
* z.B. ist eine Variable `x` von Typ `int` eine Ganzzahl
|
||||
* ähnlich zur mathematischen Schreibweise $x \in \mathbb{Z}$
|
||||
* In der Informatik nennt man das **Typisierung**
|
||||
* Es gibt verschiedene Arten der Typisierung
|
||||
|
||||
---
|
||||
|
||||
## Type annotations - Typisierung
|
||||
|
||||
- **dynamische Typisierung** überprüft die gegebenen Typen zur **Laufzeit**
|
||||
- also erst wenn das Programm *läuft*
|
||||
- **statische Typisierung** überprüft die gegebenen Typen zur **Übersetzungszeit**
|
||||
- also während wir den Quellcode übersetzen
|
||||
|
||||
---
|
||||
|
||||
## Type annotations - Typisierung
|
||||
|
||||
- **dynamische Typisierung** überprüft die gegebenen Typen zur **Laufzeit**
|
||||
- also erst wenn das Programm *läuft*
|
||||
- **statische Typisierung** überprüft die gegebenen Typen zur **Übersetzungszeit**
|
||||
- also während wir den Quellcode übersetzen
|
||||
|
||||
### Was ist nun Python?
|
||||
|
||||
---
|
||||
|
||||
### Was ist nun Python?
|
||||
|
||||
- **dynamisch typisiert**
|
||||
- wir müssen unsere `.py` Datei ausführen bevor wir wissen ob alles korrekt ist
|
||||
- **Pylance** ist ein eigenes Programm
|
||||
- es soll beim Schreiben bereits **Typverletzungen** erkennen
|
||||
- **unvollständige** Typüberprüfung, es soll nur den Entwickler unterstützen
|
||||
|
||||
---
|
||||
|
||||
## Variabeln Typannotieren
|
||||
|
||||
* `variable_name: <Type> = ...`
|
||||
* Beispiele:
|
||||
```python
|
||||
x: int = 3
|
||||
y: int = 5
|
||||
string: str = "Hello World!"
|
||||
|
||||
# aber auch eigene Objekte (OOP)
|
||||
point: Point = Point(3, 1)
|
||||
```
|
||||
* diese Annotation ist für uns **optional**
|
||||
|
||||
---
|
||||
|
||||
## Funktionen Typannotieren
|
||||
|
||||
* `def func_name(param1: <Type>, param2: <Type>, ...) -> <Type>`
|
||||
* Beispiele:
|
||||
```python
|
||||
def add(x: int, y: int) -> int:
|
||||
return x + y
|
||||
|
||||
def div(x: float, y: float) -> Optional[float]:
|
||||
if y == 0.0:
|
||||
return None
|
||||
return x / y
|
||||
```
|
||||
* diese Annotation ist **verpflichtend** und muss so vollständig wie möglich sein
|
||||
|
||||
---
|
||||
|
||||
## Klassen Typannotieren
|
||||
|
||||
*
|
||||
```
|
||||
class ClassName:
|
||||
attribute_name1: <Type>
|
||||
attribute_name2: <Type>
|
||||
...
|
||||
```
|
||||
* Beispiel:
|
||||
```python
|
||||
@dataclass
|
||||
class Point:
|
||||
x: int
|
||||
y: int
|
||||
```
|
||||
* diese Annotation ist **verpflichtend** und muss so vollständig wie möglich sein
|
||||
|
||||
---
|
||||
|
||||
## Methoden Typannotieren
|
||||
|
||||
* `def method_name(self, param1: <Type>, ...) -> <Type>`
|
||||
* Beispiel:
|
||||
```python
|
||||
class Point:
|
||||
x: int
|
||||
y: int
|
||||
|
||||
def distance_from(self, other: 'Point') -> float:
|
||||
return math.sqrt((other.x - self.x) ** 2 + (other.y - self.y) ** 2)
|
||||
```
|
||||
* `self` muss **nicht** Typannotiert werden, kann aber
|
||||
* `other` hingegen schon, wegen Python muss in der Klasse mit `'` annotiert werden
|
||||
* diese Annotation ist **verpflichtend**
|
||||
|
||||
---
|
||||
|
||||
## Datentypen von Datentypen
|
||||
|
||||
* Manche Datentypen bauen sich aus anderen Datentypen auf
|
||||
* z.B. `list` ist eine Liste von Elementen mit einem Typ
|
||||
* hierfür verwenden wir `[]` um den Datentyp in `list` zu annotieren
|
||||
```python
|
||||
def sum(xs: list[int]) -> int:
|
||||
total: int = 0
|
||||
for x in xs:
|
||||
total += x
|
||||
return total
|
||||
```
|
||||
* hierbei ist es wichtig so genau wie möglich zu annotieren!
|
||||
* diese Annotation ist **verpflichtend**
|
||||
|
||||
---
|
||||
|
||||
## Häufige Fehler mit verschachtelten Typen
|
||||
|
||||
---
|
||||
|
||||
## Fehlerquelle - `tuple[...]`
|
||||
|
||||
* Tuple haben eine feste größe
|
||||
* Tuple sind endlich
|
||||
* Tuple können Elemente mit unterschiedlichen Typen haben
|
||||
* Die Datentypen der Elemente werden mit einem `,` in `[]` getrennt
|
||||
* Beispiel:
|
||||
```python
|
||||
tup: tuple[int, int, float, str] = (1, 2, 3.0, "hello world")
|
||||
```
|
||||
* Diese Annotation ist **verpflichtend**
|
||||
|
||||
---
|
||||
|
||||
## Fehlerquelle - `dict[...]`
|
||||
|
||||
* Dictionary haben genau zwei zu definierende Typen
|
||||
* **Key**
|
||||
* **Value**
|
||||
* Beispiel:
|
||||
```python
|
||||
number_dictionary: dict[int, str] = {
|
||||
0: "zero",
|
||||
1: "one",
|
||||
2: "two",
|
||||
}
|
||||
```
|
||||
* Diese Annotation ist **verpflichtend**
|
||||
* Diese kann weiter geschachtelt werden durch z.B. `list` als `Value`:
|
||||
* `dict[int, list[str]]`
|
||||
|
||||
---
|
||||
|
||||
## Fehlerquelle - Typvariabeln (generische Typen)
|
||||
|
||||
* manchmal wollen wir nicht genau wissen welchen Datentypen wir haben
|
||||
* dieser wird dann implizit von Python erkannt
|
||||
* wir stellen damit sicher dass eine Typvariable **beliebig** aber **fest** ist
|
||||
* Beispiel:
|
||||
```python
|
||||
def add[T](x: T, y: T) -> T:
|
||||
return x + y
|
||||
```
|
||||
* `T` kann nur ein Datentyp sein, also muss `type(x) == type(y)` gelten
|
||||
* **außer** wir schrenken `T` mit `|` ein: `T: (int | str)` damit müssen x und y nicht den gleichen Datentypen haben
|
||||
* `T` lässt sich weiter einschränken durch `T: (int, str)`, hierbei ist `T` entweder ein `int` oder (exklusiv) `str`
|
||||
|
||||
---
|
||||
|
||||
## Fehlerquelle - Was ist TypeVar?
|
||||
|
||||
* `TypeVar` ist aus früheren Python-Versionen
|
||||
* Typvariablen wurden vor der Python 3.12 so definiert:
|
||||
```python
|
||||
T = TypeVar('T')
|
||||
```
|
||||
* sieht dumm aus, ist es auch, benutzt es nicht!
|
||||
|
||||
---
|
||||
|
||||
## Fragen zu Typannotationen?
|
||||
|
||||
---
|
||||
|
||||
# Funktionale Programmierung
|
||||
|
||||
---
|
||||
|
||||
## Funktionale Programmierung - was ist das?
|
||||
|
||||
- Funktionen sind äquivalent zu Datenobjekten
|
||||
- anonyme Funktionen aka Lambdas
|
||||
- Closures
|
||||
- Programmablauf mit Verkettung und Komposition von Funktionen
|
||||
|
||||
---
|
||||
|
||||
## Funktionen sind Datenobjekte
|
||||
|
||||
- Jede Funktion hat den Datentyp `Callable`
|
||||
- Wir können Funktionen wie alle anderen Objekte variabeln zuweisen
|
||||
```python
|
||||
def add(a: int, b: int) -> int:
|
||||
return a + b
|
||||
|
||||
add_but_variable = add
|
||||
|
||||
print(add_but_variable(3, 2)) # 5
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Anonyme Funktionen - `lambda`
|
||||
|
||||
- Mit dem `lambda` Keyword lassen sich anonyme Funktionen definieren ohne `def`
|
||||
- Bietet sich vor allem an für kleine Funktionen und Kompositionen von Funktionen
|
||||
```python
|
||||
print(reduce(lambda x, y: x + y, [1, 2, 3, 4])) # 10
|
||||
```
|
||||
- hat als Datentyp auch `Callable`
|
||||
```python
|
||||
add: Callable[[int, int], int] = lambda x, y: x + y
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Closures
|
||||
|
||||
- Verkettete Funktionen, bei denen die Variabeln aus vorherigen benutzt werden können
|
||||
```python
|
||||
def poly(x: float) -> Callable[[float, float], Callable[[float], float]]:
|
||||
return lambda a, b: lambda c: a * x ** 2 + b * x + c
|
||||
|
||||
print(poly(3)(2, 3)(5)) # 2 * 3 ** 2 + 3 * 3 + 5 = 32
|
||||
```
|
||||
- kein wirklich schönes Beispiel, ein besseres ist `compose` für Kompositionen
|
||||
|
||||
---
|
||||
|
||||
## Komposition
|
||||
|
||||
- Verketten von Funktionen
|
||||
```python
|
||||
def compose[T](*funcs: Callable[[T], T]) -> Callable[[T], T]:
|
||||
return fold(lambda f, g: lambda n: f(g(n)), funcs)
|
||||
|
||||
f: Callable[[int], int] = lambda n: n + 42
|
||||
g: Callable[[int], int] = lambda n: n ** 2
|
||||
h: Callable[[int], int] = lambda n: n - 3
|
||||
|
||||
print(compose(f, g, h)(0))
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Higher-Order Functions
|
||||
|
||||
- nehmen eine oder mehrere `Callable` als Argument
|
||||
- geben ein `Callable` zurück
|
||||
|
||||
### Higher-Order-Function - `map`
|
||||
|
||||
- Wendet ein `Callable` auf jedes Element in einem `Iterable` an
|
||||
|
||||
```python
|
||||
def map[T, R](func: Callable[[T], R], xs: Iterable[T]) -> Iterable[R]:
|
||||
return [func(x) for x in xs]
|
||||
|
||||
numeric_list = list(map(lambda e: int(e), ['1', '2', '3']))
|
||||
print(numeric_list) # [1, 2, 3]
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Higher-Order-Function - `filter`
|
||||
|
||||
- `filter` verarbeitet Datenstrukturen anhand eines Prädikats (`Callable`)
|
||||
- behält nur Elemente die das Prädikat erfüllen
|
||||
```python
|
||||
def filter[T](predicate: Callable[[T], bool], xs: Iterable[T]) -> Iterable[T]:
|
||||
return [x for x in xs if predicate(x)]
|
||||
|
||||
predicate: Callable[[int | None] bool] = lambda e: e is not None
|
||||
none_free_list: list[int] = list(filter(predicate, [1, 2, 3, None, 5, 6]))
|
||||
print(none_free_list) # [1, 2, 3, 5, 6] - kein None
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Higher-Order-Function - `fold`
|
||||
|
||||
- Kombiniert Elemente einer Datenstruktur
|
||||
```python
|
||||
def fold[T](func: Callable[[T, T], T], xs: Iterable[T]) -> T:
|
||||
it: Iterator[T] = iter(xs)
|
||||
value: T | None = None
|
||||
for x in it:
|
||||
match value:
|
||||
case None:
|
||||
value = x
|
||||
case _:
|
||||
value = func(value, x)
|
||||
if not value:
|
||||
raise TypeError("can't fold empty list")
|
||||
return value
|
||||
|
||||
sum: Callable[[Iterable[int]], int] = lambda xs: fold(lambda x, y: x + y, xs)
|
||||
print(sum([1, 2, 3, 4])) # 10
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### keine Higher-Order-Function - `flatten`
|
||||
|
||||
- Nimmt mehrdimensionale Listen und macht eine Liste draus
|
||||
```python
|
||||
def flatten(xs: Iterable[Any]) -> Iterable[Any]:
|
||||
new_list = []
|
||||
for s in xs:
|
||||
if isinstance(s, Iterable):
|
||||
new_list += flatten(s)
|
||||
else:
|
||||
new_list.append(s)
|
||||
return new_list
|
||||
|
||||
flattened = list(flatten([[1, 2, 3], 4, [[5, 6], 7, [8, 9]]]))
|
||||
print(flattened) # [1, 2, 3, 4, 5, 6, 7, 8, 9]
|
||||
```
|
||||
- nimmt weder `Callable` als Argumente
|
||||
- gibt kein `Callable` zurück
|
||||
- ist keine Higher-Order-Function
|
||||
|
||||
---
|
||||
|
||||
# Fragen zur funktionalen Programmierung?
|
||||
|
||||
---
|
||||
|
||||
# Weitere allgemeine Fragen?
|
@ -1,153 +0,0 @@
|
||||
---
|
||||
marp: true
|
||||
paginate: true
|
||||
# class: invert
|
||||
theme: rose-pine
|
||||
footer: Tutorium 14 - 02.02.2024 - Nils Pukropp - https://s.narl.io/s/tutorium-14
|
||||
header:
|
||||
math: mathjax
|
||||
---
|
||||
|
||||
# Tutorium 14 - 02.02.2024
|
||||
|
||||
Decorator, Testing
|
||||
|
||||
---
|
||||
|
||||
# Decorator
|
||||
|
||||
- **Design-Pattern**, oft auch **Wrapper** genannt
|
||||
- Verpackt ein Objekt um **zusätzliche Funktionalität** zu bieten
|
||||
- Funktionen sind auch Objekte
|
||||
- eine Klasse ist ein Objekt
|
||||
- Oft einfach **syntax sugar**
|
||||
|
||||
---
|
||||
## Beispiel - execute_two_times
|
||||
|
||||
```python
|
||||
def execute_two_times(fn: Callable[..., Any]) -> Callable[..., Any]:
|
||||
def wrapper(*args, **kwargs)
|
||||
fn(*args, **kwargs)
|
||||
fn(*args, **kwargs)
|
||||
return wrapper
|
||||
return wrapper
|
||||
|
||||
@execute_two_times()
|
||||
def print_two_times(msg: str):
|
||||
print(msg)
|
||||
|
||||
print_two_times("hello") # hello
|
||||
# hello
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Beispiel - execute_by
|
||||
|
||||
```python
|
||||
def execute_by(n: int):
|
||||
def wrapper(fn):
|
||||
def wrapped_fn(*args, **kwargs):
|
||||
for _ in range(0, n):
|
||||
fn(*args, **kwargs)
|
||||
return wrapped_fn
|
||||
return wrapped_fn
|
||||
return wrapper
|
||||
|
||||
@execute_by(10)
|
||||
def print_ten_times(msg: str):
|
||||
print(msg)
|
||||
|
||||
print_ten_times("hello") # hello
|
||||
# hello
|
||||
# ... (10 mal)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Beispiel - CommandExecutor
|
||||
|
||||
```python
|
||||
class CommandExecutor[R]:
|
||||
|
||||
def __init__(self):
|
||||
self.__commands: dict[str, Callable[..., R]] = {}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Beispiel - run
|
||||
|
||||
```python
|
||||
def run(self, name: str, *args, **kwargs) -> list[R]:
|
||||
results : list[R] = []
|
||||
for command_name, command in self.__commands.items():
|
||||
if command_name == name:
|
||||
results += [command(*args, **kwargs)]
|
||||
return results
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Beispiel - register
|
||||
|
||||
```python
|
||||
def register(self, cmd: Callable[..., R]) -> Callable[..., R]:
|
||||
self.__commands[cmd.__name__] = cmd
|
||||
return cmd
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Beispiel - CommandExecutor
|
||||
|
||||
```python
|
||||
class CommandExecutor[R]:
|
||||
def __init__(self):
|
||||
self.__commands: dict[str, Callable[..., R]] = {}
|
||||
|
||||
def run(self, name: str, *args, **kwargs) -> list[R]:
|
||||
results : list[R] = []
|
||||
for command_name, command in self.__commands.items():
|
||||
if command_name == name:
|
||||
results += [command(*args, **kwargs)]
|
||||
return results
|
||||
|
||||
def register(self, cmd: Callable[..., R]) -> Callable[..., R]:
|
||||
self.__commands[cmd.__name__] = cmd
|
||||
return cmd
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Beispiel - How to use
|
||||
|
||||
```python
|
||||
app = CommandExecutor[str]()
|
||||
|
||||
@app.register
|
||||
def hello_world() -> str:
|
||||
return 'hello_world'
|
||||
|
||||
@app.register
|
||||
def divide(a: int, b: int) -> str:
|
||||
if b == 0:
|
||||
return "tried to divide by zero"
|
||||
return str(a / b)
|
||||
|
||||
print(app.run('hello_world'))
|
||||
print(app.run('divide', 5, 0))
|
||||
print(app.run('divide', 10, 2))
|
||||
```
|
||||
---
|
||||
|
||||
## Decorator in der Klausur
|
||||
|
||||
- Waren noch nie Bestandteil der Klausur
|
||||
- Mut zur Lücke
|
||||
- Kann euch natürlich nichts versprechen
|
||||
|
||||
---
|
||||
|
||||
# Testen mit `pytest`
|
@ -1,36 +0,0 @@
|
||||
from typing import Callable, Iterator
|
||||
|
||||
|
||||
class CommandExecutor[R]:
|
||||
def __init__(self):
|
||||
self.__commands: dict[str, Callable[..., R]] = {}
|
||||
|
||||
def run(self, name: str, *args, **kwargs) -> list[R]:
|
||||
results : list[R] = []
|
||||
for command_name, command in self.__commands.items():
|
||||
if command_name == name:
|
||||
results += [command(*args, **kwargs)]
|
||||
return results
|
||||
|
||||
def register(self, cmd: Callable[..., R]) -> Callable[..., R]:
|
||||
self.__commands[cmd.__name__] = cmd
|
||||
return cmd
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
app = CommandExecutor[str]()
|
||||
|
||||
@app.register
|
||||
def hello_world() -> str:
|
||||
return 'hello_world'
|
||||
|
||||
@app.register
|
||||
def divide(a: int, b: int) -> str:
|
||||
if b == 0:
|
||||
return "tried to divide by zero"
|
||||
return str(a / b)
|
||||
|
||||
print(app.run('hello_world'))
|
||||
print(app.run('divide', 5, 0))
|
||||
print(app.run('divide', 10, 2))
|
||||
|
@ -1,67 +0,0 @@
|
||||
from typing import Any, Callable
|
||||
|
||||
|
||||
def count_calls[T](func: Callable[..., T]) -> Callable[..., T]:
|
||||
count_calls.calls = 0
|
||||
def wrapper(*args, **kwargs) -> T:
|
||||
init_calls = count_calls.calls
|
||||
count_calls.calls += 1
|
||||
result = func(*args, **kwargs)
|
||||
wrapper.calls = count_calls.calls - init_calls
|
||||
return result
|
||||
return wrapper
|
||||
|
||||
def f(x: int, y: int) -> int:
|
||||
if x % 2 == 0:
|
||||
return x // 2
|
||||
else:
|
||||
return x + 2 * y - 1
|
||||
|
||||
def count_iterations(a: int, b: int) -> int:
|
||||
f_a(a, a, b)
|
||||
return f_a.calls - 1
|
||||
|
||||
@count_calls
|
||||
def f_a(init: int, a: int, b: int) -> None:
|
||||
if a < b:
|
||||
return
|
||||
return f_a(init, f(a, init), b)
|
||||
|
||||
def execute_by(n: int):
|
||||
def wrapper(fn):
|
||||
def wrapped_fn(*args, **kwargs):
|
||||
for _ in range(0, n):
|
||||
fn(*args, **kwargs)
|
||||
return wrapped_fn
|
||||
return wrapped_fn
|
||||
return wrapper
|
||||
|
||||
@execute_by(10)
|
||||
def hello_world():
|
||||
print('hello world!')
|
||||
|
||||
@execute_by(10)
|
||||
def print_ten_times(msg: str):
|
||||
print(msg)
|
||||
|
||||
|
||||
def execute_two_times(fn) -> Callable[..., Any]:
|
||||
def wrapper(*args, **kwargs):
|
||||
for _ in range(0, 2):
|
||||
fn(*args, **kwargs)
|
||||
return wrapper
|
||||
return wrapper
|
||||
|
||||
@execute_two_times
|
||||
def test(msg: str):
|
||||
print(msg)
|
||||
|
||||
if __name__ == '__main__':
|
||||
assert (i := count_iterations(7, 6)) == 3, i
|
||||
assert (i := count_iterations(3, 2)) == 4, i
|
||||
assert (i := count_iterations(13, 9)) == 18, i
|
||||
assert (i := count_iterations(13, 10)) == 8, i
|
||||
assert (i := count_iterations(3, 4)) == 0, i
|
||||
print_ten_times("hello world")
|
||||
test("hello")
|
||||
|
@ -1,185 +0,0 @@
|
||||
---
|
||||
marp: true
|
||||
paginate: true
|
||||
# class: invert
|
||||
theme: rose-pine
|
||||
footer: Tutorium 15 - 09.02.2024 - Nils Pukropp - https://s.narl.io/s/tutorium-15
|
||||
header:
|
||||
math: mathjax
|
||||
---
|
||||
|
||||
# Tutorium 15 - 19.02.2024
|
||||
|
||||
Orga, Test-Exam, Regex (Exkurs)
|
||||
|
||||
---
|
||||
|
||||
# Orga
|
||||
|
||||
---
|
||||
|
||||
## Orga - Punkte, Vorstellen und einscannen
|
||||
|
||||
- Ich habe bei **allen** auf Blatt 12 (oder dem letzten korrigierten) `+6p` für das **verpasste Tutorium** vergeben
|
||||
- Ich habe für **heute** bereits allen die **Anwesenheitspunkte + Vorstellen** eingetragen
|
||||
|
||||
Alle auf die das Zutrifft sind:
|
||||
|
||||
`as2037, at359, au56, aw616, bo35, cl393, dk446, eh224, eh295, fk439, fv100, ib180, jb1484, jx20, lf409, ln200, lp269, lp321, ls818, mk1518, mr824, mt367, mw793, mz144, mz242, nm320, no43, pk375, rh295, rl173, rw208, sn205, tr211, ua28, vb202, vb205, vr110, yp39, zj11`
|
||||
|
||||
Bei Problemen oder Rückfragen einfach per mail [nils@narl.io](mailto:nils@narl.io) oder nach dem Tutorium
|
||||
|
||||
---
|
||||
|
||||
## Orga - Klausur
|
||||
|
||||
- Klausur am 19.02.
|
||||
- Es gibt vorraussichtlich zwei Termine
|
||||
- 2 Stunden
|
||||
- keine unterschiedlichen Klausuren
|
||||
- Wo, Wann?
|
||||
- individuell
|
||||
- https://courses.laurel.informatik.uni-freiburg.de/courses/2023WS-EiP/exam
|
||||
- https://s.narl.io/s/termin
|
||||
- Klausurumgebung ausprobieren unter
|
||||
- https://bwlehrpool.ruf.uni-freiburg.de/guacamole
|
||||
- https://s.narl.io/s/examvm
|
||||
|
||||
---
|
||||
|
||||
## Orga - Vorbereitung auf Klausur
|
||||
|
||||
- Macht Altklausuren
|
||||
- Übungsaufgaben im Git
|
||||
- https://git.narl.io/nvrl/eidp-klausuraufgaben-2023
|
||||
- https://s.narl.io/s/eidp-ub
|
||||
- Wenn ihr die Probeklausur gut hinbekommen habt (**auch Zeitlich!!!**) seid ihr eig safe
|
||||
- Zusatztutorium mit Dani und mir
|
||||
|
||||
---
|
||||
|
||||
## Orga - Zusatztutorium von Dani und mir
|
||||
|
||||
- Wir machen Altklausuren/Übungsaufgaben
|
||||
- Zu zweit kann man sich etwas persönlicher kümmern
|
||||
- Gibt obv. keine Punkte, wir machen das auch nur freiwillig
|
||||
- Wann, Wo?
|
||||
- Mittwoch
|
||||
- x.xx Uhr open end
|
||||
- Hier in 101
|
||||
- Es folgt auch noch eine E-Mail an alle über dessen Uni-Mail mit allen Infos
|
||||
|
||||
---
|
||||
|
||||
# Test-Exam
|
||||
|
||||
---
|
||||
|
||||
## Test-Exam - Datenklassen
|
||||
|
||||
- Ihr könnt **private** Attribute nicht in einer Unterklasser verwenden!
|
||||
- Mit `super().post_init(...)` könnt ihr diese trotzdem setzen
|
||||
- `self.__privet_attribute` in einer Unterklasse führt zu einem Fehler
|
||||
- Es gibt `protected` welches von Außen nicht sichtbar ist, aber in Unterklassen
|
||||
- `_protected_attribute` welche mit einem `_` annotiert werden
|
||||
- Beißt sich leider etwas mit `InitVar[...]` von `dataclasses`
|
||||
- Vergesst am besten `private`, `public` für die Klausur :) versprechen kann ich aber nichts
|
||||
|
||||
---
|
||||
|
||||
## Test-Exam - Automata
|
||||
|
||||
- Bitte kein `T` oder Trash State in der Klausur, außer es ist explizit gefordert
|
||||
- Ein State bei dem invalide Eingaben hingeschoben werden
|
||||
- Auch wenn das die Musterlösung von Exercise-13 gemacht hat
|
||||
- Und auch wenn es eigentlich sinnvoller ist, weil wir wollen nicht bei einer falschen Eingabe dass unser Programm abstürzt
|
||||
```python
|
||||
class State(Enum):
|
||||
q0 = auto()
|
||||
q1 = auto()
|
||||
q2 = auto()
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Test-Exam - Automata
|
||||
|
||||
```python
|
||||
def delta(state: State, input: str) -> State:
|
||||
match state, input:
|
||||
case State.q0, "a":
|
||||
return State.q1
|
||||
case State.q0, "b":
|
||||
return State.q2
|
||||
case State.q1, "a":
|
||||
return State.q0
|
||||
case State.q1, "b":
|
||||
return State.q1
|
||||
case State.q2, "a":
|
||||
return State.q2
|
||||
case State.q2, "b":
|
||||
return State.q1
|
||||
case _:
|
||||
raise ValueError("invalid state or input")
|
||||
```
|
||||
---
|
||||
|
||||
# ReGex
|
||||
|
||||
---
|
||||
|
||||
## Was ist ein ReGex?
|
||||
|
||||
- Ein regulärer Ausdruck ist ein **match pattern** in einem **text**
|
||||
- Genau gesagt bildet es eine Menge von Zeichenketten (eine **Sprache**)
|
||||
- Ihr habt bereits ReGex benutzt
|
||||
- Wenn ihr z.B. im Browser Ctrl+F drückt und nach einem Wort sucht
|
||||
- das Wort ist dann ein ReGex
|
||||
- Es gibt aber auch deutlich komplexere ReGex
|
||||
|
||||
---
|
||||
|
||||
## Automaten schon wieder
|
||||
|
||||
- Was ist wenn wir einen Eingabe-String überprüfen wollen ob er
|
||||
- mit `a` beginnt
|
||||
- dann mindest ein, aber beliebig viele `b` folgen
|
||||
- und mit einem `a` endet
|
||||
- Wir können einen Akzeptor zeichnen! (nicht-deterministischen endlichen Automaten mit akzeptierenden Zustand)
|
||||
|
||||
|
||||
---
|
||||
|
||||

|
||||
|
||||
---
|
||||
|
||||
## ReGex - Python
|
||||
|
||||
- In Python haben wir `re` also Modul
|
||||
- Ein ReGex ist eine Zeichenkette
|
||||
- `"ab"` akzeptiert `"ab"`
|
||||
- `re.fullmatch(r"ab", "ab")`
|
||||
- Es gibt Sonderzeichen wie `*, +, (, ), ...` mit denen man komplexere Eingaben überprüfen kann
|
||||
- Wir wollen `"ab...a"` von der vorherigen Slide matchen
|
||||
- `b*` möchte 0 bis unendlich `b`
|
||||
- `b+` möchte 1 bis unendlich `b`
|
||||
- also `re.fullmatch(r"ab+a", "abbbbbbba")` ist ein Match
|
||||
|
||||
---
|
||||
|
||||
## Weiter Sonderzeichen/Variabeln
|
||||
|
||||
- Mit `\d` kann man in Python eine beliebige Zahl meinen
|
||||
- Mit `\s` kann man ein beliebigen Whitespace meinen
|
||||
- So kann man z.B. eine beliebige Ip so darstellen
|
||||
- `r'(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})'`
|
||||
- Nützlich zum Parsen oder auch Testen
|
||||
- Ich nutze z.b. ReGex um eure Aufgaben zu testen
|
||||
|
||||
---
|
||||
|
||||
# Viel Erfolg bei der Klausur!
|
||||
|
||||
|
||||
|
Before Width: | Height: | Size: 7.1 KiB |
@ -1,51 +0,0 @@
|
||||
from dataclasses import dataclass
|
||||
|
||||
# (a) Vehicle
|
||||
|
||||
|
||||
@dataclass
|
||||
class Vehicle:
|
||||
seats: int
|
||||
hp: int
|
||||
ccm: int
|
||||
weight: int
|
||||
|
||||
def __post_init__(self):
|
||||
assert 0 < self.seats < 10
|
||||
assert 0 < self.hp
|
||||
assert 0 < self.ccm
|
||||
assert 0 < self.weight
|
||||
|
||||
def fun_factor(self) -> float:
|
||||
return (10 * self.hp + self.ccm) / self.weight
|
||||
|
||||
def __gt__(self, other: 'Vehicle') -> bool:
|
||||
return self.fun_factor() > other.fun_factor()
|
||||
|
||||
# (b) Car
|
||||
|
||||
|
||||
@dataclass
|
||||
class Car(Vehicle):
|
||||
spoiler: bool
|
||||
|
||||
def fun_factor(self) -> float:
|
||||
return super().fun_factor() + (0.2 if self.spoiler else 0)
|
||||
|
||||
|
||||
# (c) Motorcycle
|
||||
|
||||
|
||||
@dataclass
|
||||
class Motorcycle(Vehicle):
|
||||
sidecar: bool
|
||||
|
||||
def __post_init__(self):
|
||||
super().__post_init__()
|
||||
if self.sidecar:
|
||||
assert 2 <= self.seats <= 3
|
||||
else:
|
||||
assert 1 <= self.seats <= 2
|
||||
|
||||
def fun_factor(self) -> float:
|
||||
return super().fun_factor() * (2.4 if self.sidecar else 3)
|
@ -1,84 +0,0 @@
|
||||
from dataclasses import InitVar, dataclass
|
||||
|
||||
# (a) Vehicle
|
||||
|
||||
|
||||
@dataclass
|
||||
class Vehicle:
|
||||
_seats: InitVar[int]
|
||||
_hp: InitVar[int]
|
||||
_ccm: InitVar[int]
|
||||
_weight: InitVar[int]
|
||||
|
||||
def __post_init__(self, seats: int, hp: int, ccm: int, weight: int):
|
||||
assert 0 < seats < 10
|
||||
assert 0 < hp
|
||||
assert 0 < ccm
|
||||
assert 0 < weight
|
||||
self.__seats = seats
|
||||
self.__hp = hp
|
||||
self.__ccm = ccm
|
||||
self.__weight = weight
|
||||
|
||||
def fun_factor(self) -> float:
|
||||
return (10 * self.__hp + self.__ccm) / self.__weight
|
||||
|
||||
def __gt__(self, other: 'Vehicle') -> bool:
|
||||
return self.fun_factor() > other.fun_factor()
|
||||
|
||||
@property
|
||||
def seats(self) -> int:
|
||||
return self.__seats
|
||||
|
||||
@property
|
||||
def hp(self) -> int:
|
||||
return self.__hp
|
||||
|
||||
@property
|
||||
def ccm(self) -> int:
|
||||
return self.__ccm
|
||||
|
||||
@property
|
||||
def weight(self) -> int:
|
||||
return self.__weight
|
||||
|
||||
# (b) Car
|
||||
|
||||
|
||||
@dataclass
|
||||
class Car(Vehicle):
|
||||
_spoiler: InitVar[bool]
|
||||
|
||||
def __post_init__(self, seats: int, hp: int, ccm: int, weight: int, spoiler: bool):
|
||||
super().__post_init__(seats, hp, ccm, weight)
|
||||
self.__spoiler = spoiler
|
||||
|
||||
def fun_factor(self) -> float:
|
||||
return super().fun_factor() + (0.2 if self.__spoiler else 0)
|
||||
|
||||
@property
|
||||
def has_spoiler(self) -> bool:
|
||||
return self.__spoiler
|
||||
|
||||
|
||||
# (c) Motorcycle
|
||||
|
||||
|
||||
@dataclass
|
||||
class Motorcycle(Vehicle):
|
||||
_sidecar: InitVar[bool]
|
||||
|
||||
def __post_init__(self, seats: int, hp: int, ccm: int, weight: int, sidecar: bool):
|
||||
if sidecar:
|
||||
assert 2 <= seats <= 3
|
||||
else:
|
||||
assert 1 <= seats <= 2
|
||||
super().__post_init__(seats, hp, ccm, weight)
|
||||
self.__sidecar = sidecar
|
||||
|
||||
def fun_factor(self) -> float:
|
||||
return super().fun_factor() * (2.4 if self.__sidecar else 3)
|
||||
|
||||
@property
|
||||
def is_sidecar(self) -> bool:
|
||||
return self.__sidecar
|