Compare commits
8 Commits
1121c6a171
...
main
Author | SHA1 | Date | |
---|---|---|---|
1efd3b66b0 | |||
483c6ee562 | |||
6f733ce98e | |||
c83f33cfca | |||
c7c1f3ea98 | |||
4c5d27e8d1 | |||
36b6a78833 | |||
14cd9fe8b0 |
@@ -1,4 +1,4 @@
|
|||||||
# Übungsaufgaben zur EidP (WS2023) Klausur
|
# Übungsaufgaben zur EidP (WS2024) Klausur
|
||||||
|
|
||||||
Alle Aufgaben hier behandeln Konzepte aus der Vorlesung **Einführung in die Programmierung** von der Albert-Ludwig-Universität Freiburg. Hierbei handelt es sich um selbsterstellte Aufgaben der EidP-Tutoren [**Nils Pukropp**](mailto:nils@narl.io) und [**Daniel Mironow**](mailto:mail@danielmironov.dev) die bei der Vorbereitung auf die Klausur helfen sollen. :D
|
Alle Aufgaben hier behandeln Konzepte aus der Vorlesung **Einführung in die Programmierung** von der Albert-Ludwig-Universität Freiburg. Hierbei handelt es sich um selbsterstellte Aufgaben der EidP-Tutoren [**Nils Pukropp**](mailto:nils@narl.io) und [**Daniel Mironow**](mailto:mail@danielmironov.dev) die bei der Vorbereitung auf die Klausur helfen sollen. :D
|
||||||
|
|
||||||
@@ -44,13 +44,6 @@ Es gibt keine direkte Reihenfolge, lediglich Themen die sich teilweise überschn
|
|||||||
- Zunächst braucht Ihr `pytest` welches ihr mit `pip install -m pytest` installieren könnt
|
- Zunächst braucht Ihr `pytest` welches ihr mit `pip install -m pytest` installieren könnt
|
||||||
- Könnt auch gerne nachfragen wenn was nicht funktioniert!
|
- Könnt auch gerne nachfragen wenn was nicht funktioniert!
|
||||||
- Dann könnt ihr einfach die Tests mit `pytest` in der Konsole aufrufen
|
- Dann könnt ihr einfach die Tests mit `pytest` in der Konsole aufrufen
|
||||||
- Schlagen die Tests fehl sieht das so aus:
|
|
||||||
|
|
||||||

|
|
||||||
- Hier sagt euch Pytest auch was alles nicht an eurem Code funktioniert
|
|
||||||
- Funktioniert euer Code sieht das so aus:
|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
## Kontakt
|
## Kontakt
|
||||||
|
|
||||||
|
34
generics/graphen/README.md
Normal file
34
generics/graphen/README.md
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
# Graphen
|
||||||
|
|
||||||
|
## Graph-Klasse
|
||||||
|
|
||||||
|
Wir wollen hier einen Graphen modellieren, Graphen kennt ihr bereits als Bäume. Diese sind spezielle Graphen welche eine Wurzel haben, also den ersten Knoten. Und dann immer maximal zwei weitere Knoten (**left** und **right**). Die letzten Knoten nennt man auch Blätter. Allgemeine Graphen hingegen kann jeder Knoten auf beliebig viele weitere Knoten verweisen. Diese Verweise nennt man Kanten.
|
||||||
|
|
||||||
|
Unsere **Graph** Klasse modellieren wir etwas anders mit einem `dict` welcher immer einen Knoten auf eine Liste von weiteren Knoten mappt.
|
||||||
|
|
||||||
|
```python
|
||||||
|
from dataclasses import dataclass, field
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class Graph[T]:
|
||||||
|
vertecies: dict[T, list[T]] = field(default_factory=dict)
|
||||||
|
```
|
||||||
|
|
||||||
|
Als Beispiel:
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
würde dann im Code so aussehen:
|
||||||
|
|
||||||
|
```python
|
||||||
|
|
||||||
|
my_graph: Graph[str] = Graph(
|
||||||
|
vertecies={
|
||||||
|
'A': ['B', 'D'],
|
||||||
|
'B': ['A', 'C'],
|
||||||
|
'C': ['B', 'D'],
|
||||||
|
'D': ['C', 'A'],
|
||||||
|
})
|
||||||
|
|
||||||
|
```
|
60
generics/graphen/graphs.py
Normal file
60
generics/graphen/graphs.py
Normal file
@@ -0,0 +1,60 @@
|
|||||||
|
type Graph[T] = dict[T, list[T]]
|
||||||
|
|
||||||
|
|
||||||
|
def is_bidirected[T](graph: Graph[T]) -> bool:
|
||||||
|
for a, vertecies in graph.items():
|
||||||
|
for b in vertecies:
|
||||||
|
if a not in graph[b]:
|
||||||
|
return False
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
def depth_first_search[T](graph: Graph[T], node: T,
|
||||||
|
_visited: set[T] = None) -> set[T]:
|
||||||
|
if _visited is None:
|
||||||
|
_visited = set()
|
||||||
|
if node in _visited:
|
||||||
|
return set()
|
||||||
|
_visited.add(node)
|
||||||
|
for neighbours in graph[node]:
|
||||||
|
depth_first_search(graph, neighbours, _visited)
|
||||||
|
return _visited
|
||||||
|
|
||||||
|
|
||||||
|
def all_edges[T](graph: Graph[T]) -> set[tuple[T, T]]:
|
||||||
|
all_vertecies = set()
|
||||||
|
for a, vertecies in graph.items():
|
||||||
|
for b in vertecies:
|
||||||
|
all_vertecies.add((a, b))
|
||||||
|
return all_vertecies
|
||||||
|
|
||||||
|
|
||||||
|
def alt_all_edges[T](graph: Graph[T]) -> set[tuple[T, T]]:
|
||||||
|
return {(a, b)
|
||||||
|
for a, vertecies in graph.items()
|
||||||
|
for b in vertecies}
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
my_graph: Graph[str] = {
|
||||||
|
'A': ['B', 'D'],
|
||||||
|
'B': ['A', 'C'],
|
||||||
|
'C': ['B', 'D'],
|
||||||
|
'D': ['C', 'A'],
|
||||||
|
}
|
||||||
|
assert all_edges(my_graph) == {('A', 'B'), ('A', 'D'), ('B', 'A'),
|
||||||
|
('B', 'C'), ('C', 'B'), ('C', 'D'),
|
||||||
|
('D', 'C'), ('D', 'A')}
|
||||||
|
assert all_edges(my_graph) == alt_all_edges(my_graph)
|
||||||
|
assert is_bidirected(my_graph)
|
||||||
|
assert not is_bidirected({'A': ['B', 'C'], 'B': [
|
||||||
|
'C'], 'C': ['A', 'B']})
|
||||||
|
my_graph = {
|
||||||
|
0: [1, 2, 3],
|
||||||
|
1: [0],
|
||||||
|
2: [3, 4, 0],
|
||||||
|
3: [0, 2],
|
||||||
|
4: [2],
|
||||||
|
5: [],
|
||||||
|
}
|
||||||
|
print(depth_first_search(my_graph, 5))
|
BIN
generics/graphen/image.png
Normal file
BIN
generics/graphen/image.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 89 KiB |
@@ -1 +1,56 @@
|
|||||||
# List Comprehensions
|
# List Comprehensions
|
||||||
|
|
||||||
|
## Aufgabe 1 - Divisible by 7
|
||||||
|
Schreibe eine List-Comprehension welche eine Liste an Zahlen bis `n` zurückgibt, welche dividierbar durch 7 sind.
|
||||||
|
|
||||||
|
### Examples
|
||||||
|
```python
|
||||||
|
assert divisible_by_7(0) == []
|
||||||
|
assert divisible_by_7(10) == [7]
|
||||||
|
assert divisible_by_7(43) == [7, 14, 28, 35, 42]
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
## Aufgabe 2 - Contains 3
|
||||||
|
Schreibe eine List-Comprehension welche eine Liste an Zahlen bis `n` zurückgibt, welche eine `3` enthalten.
|
||||||
|
### Examples
|
||||||
|
```python
|
||||||
|
assert contains_3(10) == [3]
|
||||||
|
assert contains_3(24) == [3, 13, 23]
|
||||||
|
```
|
||||||
|
---
|
||||||
|
## Aufgabe 3 - Count Spaces
|
||||||
|
Schreibe eine Funktion, welche die Whitespaces (`' '`) in einem String zählt. Verwende hierzu List-Comprehensions.
|
||||||
|
### Examples
|
||||||
|
```python
|
||||||
|
assert count_spaces('') == 0
|
||||||
|
assert count_spaces(' ') == 1
|
||||||
|
assert count_spaces('hello, world ') == 2
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
## Aufgabe 4 - Remove Vowels
|
||||||
|
Schreibe eine Funktion, welche alle Vokale (`'AEIOUaeiou'`) aus einem String entfernt. Verwendet hierfür List-Comprehension
|
||||||
|
### Examples
|
||||||
|
```python
|
||||||
|
assert remove_vowels('') == ''
|
||||||
|
assert remove_vowels(' ') == ' '
|
||||||
|
assert remove_vowels('hello, world') == 'hll, wrld'
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
## Aufgabe 5 - Wortlängen
|
||||||
|
Schreibe eine Dictionary comprehension, welche ein Dictionary zurückgibt, wo die länge aller Wörter zu finden ist.
|
||||||
|
### Example
|
||||||
|
```python
|
||||||
|
assert word_length('this is a string') == {'this': 4, 'is': 2, 'a': 1, 'string': 6}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
## Aufgabe 6 - Primzahlen generieren (schwer)
|
||||||
|
Schreibe eine Funktion, welche ein Liste an allen Primzahlen bis `n` zurückgibt. Verwende hierzu List-Comprehensions
|
||||||
|
### Examples
|
||||||
|
```python
|
||||||
|
assert prime_numbers(100) == [2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97]
|
||||||
|
```
|
||||||
|
---
|
||||||
|
95
recursion/recursive_datastructure/README.md
Normal file
95
recursion/recursive_datastructure/README.md
Normal file
@@ -0,0 +1,95 @@
|
|||||||
|
# Rekursive Datenstrukturen
|
||||||
|
|
||||||
|
|
||||||
|
## [LinkedList](./lists.py)
|
||||||
|
|
||||||
|
In der Vorlesung wurden uns rekursive Datenstrukturen vorgestellt. Und zwar in der einfachsten Form, einer Liste. Hierbei modellieren wir eine *Node* Datenstruktur die unseren Wert *Value* trägt und auf die nächste *Node* in der Liste verweist durch *next_node*.
|
||||||
|
|
||||||
|
```python
|
||||||
|
from dataclasses import dataclass
|
||||||
|
from typing import Optional
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class Node[T]:
|
||||||
|
value: T
|
||||||
|
next_node: Optional['Node[T]']
|
||||||
|
```
|
||||||
|
|
||||||
|
Jetzt wollen wir eine Liste um unsere Node modellieren, hierbei interessiert uns nur die erste Node *__head* und die Länge *__length*.
|
||||||
|
|
||||||
|
```python
|
||||||
|
@dataclass
|
||||||
|
class LinkedList[T]:
|
||||||
|
|
||||||
|
def __post_init__(self):
|
||||||
|
self.__head: Optional[Node[T]] = None
|
||||||
|
self.__length: int = 0
|
||||||
|
```
|
||||||
|
|
||||||
|
Jetzt geht es darum alle Standardmethoden die eine Liste braucht zu implementieren.
|
||||||
|
|
||||||
|
## [BinaryTree](./trees.py)
|
||||||
|
|
||||||
|
Jetzt wollen wir eine andere rekursive Datenstruktur definieren, den binary Tree. Diese besteht immer aus zwei Abzweigungen *right* und *left*.
|
||||||
|
|
||||||
|
```python
|
||||||
|
@dataclass
|
||||||
|
class Node[T]:
|
||||||
|
value: T
|
||||||
|
left: Optional['Node[T]']
|
||||||
|
right: Optional['Node[T]']
|
||||||
|
```
|
||||||
|
|
||||||
|
Statt jetzt eine eigene Klasse zu erstellen die `Node` intern benutzt wollen wir einfach einen Typalias erstellen und imperative mit Funktionen statt Methoden arbeiten.
|
||||||
|
|
||||||
|
```python
|
||||||
|
type BinaryTree[T] = Optional[Node[T]]
|
||||||
|
```
|
||||||
|
|
||||||
|
### `traverse`
|
||||||
|
|
||||||
|
Man kann einen BinaryTree auf drei typische arten ablaufen
|
||||||
|
|
||||||
|
- Inorder
|
||||||
|
- Erst laufen wir Links ab
|
||||||
|
- Dann schauen wir uns die Node an
|
||||||
|
- Dann laufen wir Rechts ab
|
||||||
|
- Preorder
|
||||||
|
- Erst schauen wir uns die Node an
|
||||||
|
- Dann laufen wir Links ab
|
||||||
|
- Dann laufen wir Rechts ab
|
||||||
|
- Postorder
|
||||||
|
- Erst laufen wir Links ab
|
||||||
|
- Dann laufen wir Rechts ab
|
||||||
|
- Dann schauen wir uns die Node an
|
||||||
|
|
||||||
|
Hierfür erstellen wir einen `Enum` um zu unterscheiden wie wir gerade ablaufen
|
||||||
|
|
||||||
|
```python
|
||||||
|
class TraversalType(Enum):
|
||||||
|
INORDER, POSTORDER, PREORDER = auto(), auto(), auto()
|
||||||
|
```
|
||||||
|
|
||||||
|
Und jetzt implementieren wir einen Generator der uns einen Iterator erzeugt welcher den BinaryTree in der gegebenen Order abläuft.
|
||||||
|
|
||||||
|
```python
|
||||||
|
def traverse[T](tree: BinaryTree[T], order: TraversalType = TraversalType.INORDER) -> Iterator[T]:
|
||||||
|
pass
|
||||||
|
```
|
||||||
|
|
||||||
|
Tipp:
|
||||||
|
|
||||||
|
Mit `yield from ...` kann man einen ganzen Iterator *yield*en.
|
||||||
|
|
||||||
|
```python
|
||||||
|
def my_range(start: int, end: int) -> Iterator[int]:
|
||||||
|
yield from range(start, end)
|
||||||
|
```
|
||||||
|
|
||||||
|
## [BinarySearchTree](./search_trees.py) (*Schwer*)
|
||||||
|
|
||||||
|
Was wäre wenn wir nun einen Binary Tree haben, dieser aber eine Sortierung hat. Wenn wir ein Element hinzufügen packen wir alle Elemente kleiner nach Links und alle anderen nach Rechts.
|
||||||
|
|
||||||
|
[Hier eine kleine Visualisierung](https://www.cs.usfca.edu/~galles/visualization/BST.html)
|
||||||
|
|
||||||
|
Probiert einfach mal rum damit ihr euch die Funktion eines BST vorstellen könnt. Wirklich komplex wird `delete`.
|
56
recursion/recursive_datastructure/lists.py
Normal file
56
recursion/recursive_datastructure/lists.py
Normal file
@@ -0,0 +1,56 @@
|
|||||||
|
from dataclasses import dataclass
|
||||||
|
from typing import Iterator, Optional
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class Node[T]:
|
||||||
|
value: T
|
||||||
|
next_node: Optional['Node[T]']
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class LinkedList[T]:
|
||||||
|
|
||||||
|
def __post_init__(self):
|
||||||
|
# unser erstes Element
|
||||||
|
self.__head: Optional[Node[T]] = None
|
||||||
|
# Die Länge der Liste
|
||||||
|
self.__length = 0
|
||||||
|
|
||||||
|
def __eq__(self, other: 'LinkedList[T]') -> bool:
|
||||||
|
"""Hier wollen wir alle Elemente von dieser Liste mit allen Elementen von einer anderen Liste vergleichen.
|
||||||
|
Hierbei kann man von ausgehen dass T vergleichbar ist.
|
||||||
|
Das Programm sollte nicht abstürzen bei unterschiedlichen längen etc. sondern einfach False zurückgeben"""
|
||||||
|
pass
|
||||||
|
|
||||||
|
def __iter__(self) -> Iterator[T]:
|
||||||
|
"""Hier wollen wir einen Iterator über alle Elemente haben, der Iterator kann leer sein
|
||||||
|
Tipp: Generator"""
|
||||||
|
pass
|
||||||
|
|
||||||
|
def append(self, value: T) -> None:
|
||||||
|
"""Hier wollen wir einfach `value` an das Ende der Liste setzen und die Länge erhöhen"""
|
||||||
|
pass
|
||||||
|
|
||||||
|
def remove(self, value: T):
|
||||||
|
"""Hier wollen wir den Wert `value` aus unserer Liste entfernen, wir können davon ausgehen dass T vergleichbar ist.
|
||||||
|
Wenn wir `value` nicht finden soll einfach nichts passieren"""
|
||||||
|
pass
|
||||||
|
|
||||||
|
def __len__(self) -> int:
|
||||||
|
"""Hier soll die Länge der Liste zurückgegeben werden"""
|
||||||
|
pass
|
||||||
|
|
||||||
|
def __getitem__(self, index: int) -> T:
|
||||||
|
"""_summary_
|
||||||
|
|
||||||
|
__getitem__ definiert das verhalten vom [] index operator wobei `index` der Wert ist der in [] übergeben wird
|
||||||
|
|
||||||
|
Hierbei sollen wir das Item `T` an Stelle `index` zurückgeben und sonst einen IndexError raisen
|
||||||
|
|
||||||
|
Args:
|
||||||
|
index (int): der Index von dem Item was wir suchen
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
T: das Item an stelle `index`
|
||||||
|
"""
|
||||||
|
pass
|
13
recursion/recursive_datastructure/search_trees.py
Normal file
13
recursion/recursive_datastructure/search_trees.py
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
from typing import Optional
|
||||||
|
from trees import Node
|
||||||
|
|
||||||
|
type BinarySearchTree[T] = Optional[Node[T]]
|
||||||
|
|
||||||
|
def insert[T](node: BinarySearchTree[T], value: T) -> BinarySearchTree[T]:
|
||||||
|
pass
|
||||||
|
|
||||||
|
def exists[T](node: Optional[Node[T]], value: T) -> bool:
|
||||||
|
pass
|
||||||
|
|
||||||
|
def remove[T](node: BinarySearchTree[T], value: T) -> BinarySearchTree[T]:
|
||||||
|
pass
|
62
recursion/recursive_datastructure/solution/lists.py
Normal file
62
recursion/recursive_datastructure/solution/lists.py
Normal file
@@ -0,0 +1,62 @@
|
|||||||
|
from dataclasses import dataclass
|
||||||
|
from typing import Iterator, Optional
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class Node[T]:
|
||||||
|
value: T
|
||||||
|
next_node: Optional['Node[T]']
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class LinkedList[T]:
|
||||||
|
|
||||||
|
def __post_init__(self):
|
||||||
|
self.__head: Optional[Node[T]] = None
|
||||||
|
self.__length = 0
|
||||||
|
|
||||||
|
def __eq__(self, other: 'LinkedList[T]') -> bool:
|
||||||
|
if len(self) != len(other):
|
||||||
|
return False
|
||||||
|
for i in range(0, len(self)):
|
||||||
|
if self[i] != other[i]:
|
||||||
|
return False
|
||||||
|
return True
|
||||||
|
|
||||||
|
def __iter__(self) -> Iterator[T]:
|
||||||
|
node = self.__head
|
||||||
|
while node:
|
||||||
|
yield node.value
|
||||||
|
node = node.next_node
|
||||||
|
|
||||||
|
def append(self, value: T):
|
||||||
|
if not self.__head:
|
||||||
|
self.__head = Node(value, None)
|
||||||
|
else:
|
||||||
|
node = self.__head
|
||||||
|
while node.next_node:
|
||||||
|
node = node.next_node
|
||||||
|
node.next_node = Node(value, None)
|
||||||
|
self.__length += 1
|
||||||
|
|
||||||
|
def remove(self, value: T):
|
||||||
|
if self.__head and self.__head.value == value:
|
||||||
|
self.__head = self.__head.next_node
|
||||||
|
self.__length -= 1
|
||||||
|
elif self.__head:
|
||||||
|
node = self.__head
|
||||||
|
while node.next_node and node.next_node.value != value:
|
||||||
|
node = node.next_node
|
||||||
|
if node.next_node and node.next_node.value == value:
|
||||||
|
node.next_node = node.next_node.next_node
|
||||||
|
self.__length -= 1
|
||||||
|
|
||||||
|
def __len__(self) -> int:
|
||||||
|
return self.__length
|
||||||
|
|
||||||
|
def __getitem__(self, index: int) -> T:
|
||||||
|
if index >= len(self):
|
||||||
|
raise IndexError
|
||||||
|
node = self.__head
|
||||||
|
for _ in range(0, index):
|
||||||
|
node = node.next_node
|
||||||
|
return node.value
|
49
recursion/recursive_datastructure/solution/search_trees.py
Normal file
49
recursion/recursive_datastructure/solution/search_trees.py
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
from typing import Optional
|
||||||
|
from trees import Node
|
||||||
|
|
||||||
|
type BinarySearchTree[T] = Optional[Node[T]]
|
||||||
|
|
||||||
|
|
||||||
|
def insert[T](node: BinarySearchTree[T], value: T) -> BinarySearchTree[T]:
|
||||||
|
prev = None
|
||||||
|
curr = node
|
||||||
|
while curr:
|
||||||
|
prev = curr
|
||||||
|
if curr.value > value:
|
||||||
|
curr = curr.left
|
||||||
|
else:
|
||||||
|
curr = curr.right
|
||||||
|
if prev is None:
|
||||||
|
return Node(value, None, None)
|
||||||
|
elif value < prev.value:
|
||||||
|
prev.left = Node(value, None, None)
|
||||||
|
else:
|
||||||
|
prev.right = Node(value, None, None)
|
||||||
|
return node
|
||||||
|
|
||||||
|
|
||||||
|
def exists[T](node: Optional[Node[T]], value: T) -> bool:
|
||||||
|
return node and (exists(node.left, value)
|
||||||
|
if value < node.value
|
||||||
|
else (value == node.value or exists(node.right, value)))
|
||||||
|
|
||||||
|
|
||||||
|
def remove[T](node: BinarySearchTree[T], value: T) -> BinarySearchTree[T]:
|
||||||
|
if node is None:
|
||||||
|
return node
|
||||||
|
if value < node.value:
|
||||||
|
node.left = remove(node.left, value)
|
||||||
|
return node
|
||||||
|
if value > node.value:
|
||||||
|
node.right = remove(node.right, value)
|
||||||
|
return node
|
||||||
|
if node.right is None:
|
||||||
|
return node.left
|
||||||
|
if node.left is None:
|
||||||
|
return node.right
|
||||||
|
min_node = node.right
|
||||||
|
while min_node.left:
|
||||||
|
min_node = min_node.left
|
||||||
|
node.value = min_node.value
|
||||||
|
node.right = remove(node.right, min_node.value)
|
||||||
|
return node
|
77
recursion/recursive_datastructure/solution/test_lists.py
Normal file
77
recursion/recursive_datastructure/solution/test_lists.py
Normal file
@@ -0,0 +1,77 @@
|
|||||||
|
from lists import LinkedList
|
||||||
|
|
||||||
|
def test_append():
|
||||||
|
lst = LinkedList[int]()
|
||||||
|
lst.append(1)
|
||||||
|
lst.append(2)
|
||||||
|
lst.append(3)
|
||||||
|
lst.append(4)
|
||||||
|
lst2 = LinkedList[int]()
|
||||||
|
lst2.append(1)
|
||||||
|
lst2.append(2)
|
||||||
|
lst2.append(3)
|
||||||
|
lst2.append(4)
|
||||||
|
assert lst == lst2
|
||||||
|
|
||||||
|
def test_remove():
|
||||||
|
lst = LinkedList[int]()
|
||||||
|
lst.remove(0)
|
||||||
|
lst.append(1)
|
||||||
|
lst.append(2)
|
||||||
|
lst.append(3)
|
||||||
|
lst.append(4)
|
||||||
|
lst.remove(2)
|
||||||
|
lst.remove(5)
|
||||||
|
lst2 = LinkedList[int]()
|
||||||
|
lst2.append(1)
|
||||||
|
lst2.append(3)
|
||||||
|
lst2.append(4)
|
||||||
|
assert lst == lst2
|
||||||
|
|
||||||
|
def test_length():
|
||||||
|
lst = LinkedList[int]()
|
||||||
|
lst.append(0)
|
||||||
|
lst.append(1)
|
||||||
|
lst.append(2)
|
||||||
|
lst.append(3)
|
||||||
|
lst.append(4)
|
||||||
|
assert len(lst) == 5
|
||||||
|
lst.remove(0)
|
||||||
|
assert len(lst) == 4
|
||||||
|
lst.remove(2)
|
||||||
|
assert len(lst) == 3
|
||||||
|
lst.remove(1)
|
||||||
|
assert len(lst) == 2
|
||||||
|
lst.remove(3)
|
||||||
|
assert len(lst) == 1
|
||||||
|
lst.remove(4)
|
||||||
|
assert len(lst) == 0
|
||||||
|
lst.remove(4)
|
||||||
|
lst.remove(5)
|
||||||
|
assert len(lst) == 0
|
||||||
|
|
||||||
|
|
||||||
|
def test_index():
|
||||||
|
lst = LinkedList[int]()
|
||||||
|
lst.append(0)
|
||||||
|
lst.append(1)
|
||||||
|
lst.append(2)
|
||||||
|
lst.append(3)
|
||||||
|
lst.append(4)
|
||||||
|
assert lst[0] == 0
|
||||||
|
assert lst[1] == 1
|
||||||
|
assert lst[2] == 2
|
||||||
|
assert lst[3] == 3
|
||||||
|
assert lst[4] == 4
|
||||||
|
|
||||||
|
def test_iter():
|
||||||
|
lst = LinkedList[int]()
|
||||||
|
lst.append(0)
|
||||||
|
lst.append(1)
|
||||||
|
lst.append(2)
|
||||||
|
lst.append(3)
|
||||||
|
lst.append(4)
|
||||||
|
j = 0
|
||||||
|
for i in iter(lst):
|
||||||
|
assert i == j
|
||||||
|
j += 1
|
@@ -0,0 +1,51 @@
|
|||||||
|
from typing import Iterator, Optional
|
||||||
|
from search_trees import BinarySearchTree, insert, remove, exists
|
||||||
|
from trees import Node
|
||||||
|
from random import randint as random
|
||||||
|
|
||||||
|
|
||||||
|
def traverse[T](tree: Optional[Node[T]]) -> Iterator[T]:
|
||||||
|
match tree:
|
||||||
|
case Node(value, left, right):
|
||||||
|
yield from traverse(left)
|
||||||
|
yield value
|
||||||
|
yield from traverse(right)
|
||||||
|
case _:
|
||||||
|
return
|
||||||
|
|
||||||
|
|
||||||
|
MAX = 100
|
||||||
|
MIN = 0
|
||||||
|
LENGTH = 100
|
||||||
|
|
||||||
|
NUMS = {random(MIN, MAX) for _ in range(0, LENGTH)}
|
||||||
|
NUMS_FILTER = {random(MIN, MAX) for _ in range(0, LENGTH // 4)}
|
||||||
|
|
||||||
|
def test_insert():
|
||||||
|
bst: BinarySearchTree = None
|
||||||
|
for num in NUMS:
|
||||||
|
bst = insert(bst, num)
|
||||||
|
assert list(traverse(bst)) == sorted(NUMS)
|
||||||
|
|
||||||
|
|
||||||
|
def test_remove():
|
||||||
|
bst: BinarySearchTree = None
|
||||||
|
for num in NUMS:
|
||||||
|
bst = insert(bst, num)
|
||||||
|
assert list(traverse(bst)) == sorted(NUMS)
|
||||||
|
for num in NUMS_FILTER:
|
||||||
|
remove(bst, num)
|
||||||
|
|
||||||
|
assert list(traverse(bst)) == sorted(filter(lambda x: x not in NUMS_FILTER, NUMS))
|
||||||
|
|
||||||
|
|
||||||
|
def test_exists():
|
||||||
|
bst: BinarySearchTree = None
|
||||||
|
for num in NUMS:
|
||||||
|
bst = insert(bst, num)
|
||||||
|
for num in NUMS_FILTER:
|
||||||
|
remove(bst, num)
|
||||||
|
|
||||||
|
assert all(map(lambda x: exists(bst, x),
|
||||||
|
filter(lambda x: x not in NUMS_FILTER, NUMS)))
|
||||||
|
assert all(map(lambda x: not exists(bst, x), NUMS_FILTER))
|
13
recursion/recursive_datastructure/solution/test_trees.py
Normal file
13
recursion/recursive_datastructure/solution/test_trees.py
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
from trees import Node, traverse, TraversalType
|
||||||
|
|
||||||
|
|
||||||
|
def test_traverse():
|
||||||
|
tree = Node(1, Node(2, Node(3, None, None), Node(4, Node(
|
||||||
|
5, None, None), None)), Node(6, Node(7, None, None), Node(8, None, None)))
|
||||||
|
|
||||||
|
assert ", ".join(map(lambda x: str(x), traverse(tree))
|
||||||
|
) == "3, 2, 5, 4, 1, 7, 6, 8"
|
||||||
|
assert ", ".join(map(lambda x: str(x), traverse(
|
||||||
|
tree, TraversalType.PREORDER))) == "1, 2, 3, 4, 5, 6, 7, 8"
|
||||||
|
assert ", ".join(map(lambda x: str(x), traverse(
|
||||||
|
tree, TraversalType.POSTORDER))) == "3, 5, 4, 2, 7, 8, 6, 1"
|
33
recursion/recursive_datastructure/solution/trees.py
Normal file
33
recursion/recursive_datastructure/solution/trees.py
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
from dataclasses import dataclass
|
||||||
|
from enum import Enum, auto
|
||||||
|
from typing import Iterator, Optional
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class Node[T]:
|
||||||
|
value: T
|
||||||
|
left: Optional['Node[T]']
|
||||||
|
right: Optional['Node[T]']
|
||||||
|
|
||||||
|
|
||||||
|
type BinaryTree[T] = Optional[Node[T]]
|
||||||
|
|
||||||
|
class TraversalType(Enum):
|
||||||
|
INORDER, POSTORDER, PREORDER = auto(), auto(), auto()
|
||||||
|
|
||||||
|
def traverse[T](tree: BinaryTree[T], order: TraversalType = TraversalType.INORDER) -> Iterator[T]:
|
||||||
|
match (tree, order):
|
||||||
|
case (Node(value, left, right), TraversalType.INORDER):
|
||||||
|
yield from traverse(left, order)
|
||||||
|
yield value
|
||||||
|
yield from traverse(right, order)
|
||||||
|
case (Node(value, left, right), TraversalType.POSTORDER):
|
||||||
|
yield from traverse(left, order)
|
||||||
|
yield from traverse(right, order)
|
||||||
|
yield value
|
||||||
|
case (Node(value, left, right), TraversalType.PREORDER):
|
||||||
|
yield value
|
||||||
|
yield from traverse(left, order)
|
||||||
|
yield from traverse(right, order)
|
||||||
|
case _:
|
||||||
|
return
|
19
recursion/recursive_datastructure/trees.py
Normal file
19
recursion/recursive_datastructure/trees.py
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
from dataclasses import dataclass
|
||||||
|
from enum import Enum, auto
|
||||||
|
from typing import Iterator, Optional
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class Node[T]:
|
||||||
|
value: T
|
||||||
|
left: Optional['Node[T]']
|
||||||
|
right: Optional['Node[T]']
|
||||||
|
|
||||||
|
|
||||||
|
type BinaryTree[T] = Optional[Node[T]]
|
||||||
|
|
||||||
|
class TraversalType(Enum):
|
||||||
|
INORDER, POSTORDER, PREORDER = auto(), auto(), auto()
|
||||||
|
|
||||||
|
def traverse[T](tree: BinaryTree[T], order: TraversalType = TraversalType.INORDER) -> Iterator[T]:
|
||||||
|
pass
|
@@ -2,3 +2,91 @@
|
|||||||
|
|
||||||
Diese Teilaufgaben sind teilweise sehr einfach zu implementieren und sollen veranschaulichen was man alles mit Strings machen kann. Hierbei könnt ihr euch es natürlich einfach machen und die Buildins von `str` verwenden oder die Funktionen komplett selber implementieren.
|
Diese Teilaufgaben sind teilweise sehr einfach zu implementieren und sollen veranschaulichen was man alles mit Strings machen kann. Hierbei könnt ihr euch es natürlich einfach machen und die Buildins von `str` verwenden oder die Funktionen komplett selber implementieren.
|
||||||
|
|
||||||
|
## String-Class
|
||||||
|
|
||||||
|
Zunächst möchten wir eine *dataclass* *String* erstellen die genau einen *str* als Argument nimmt und in eimem privaten Attribut *__s* speichert. Dieses Attribut soll auch von Typ *str* sein und unsere *String* klasse intern representieren.
|
||||||
|
|
||||||
|
## Operatoren, Str, Len, und Iterator
|
||||||
|
|
||||||
|
Nun möchten wir alle standard Operatoren und Funktionen für unseren *String* überschreiben
|
||||||
|
|
||||||
|
### `__str__`
|
||||||
|
|
||||||
|
Diese Methode soll einfach die *str* Representation unserer *String* Klasse sein
|
||||||
|
|
||||||
|
### `__add__`, `__radd__`
|
||||||
|
|
||||||
|
Diese Methode soll unseren *String* nicht verändern, aber zwei *Str* konkatenieren. Hierbei kann ein *Str* sowohl ein *String* als auch ein *str* sein! Hierbei wird `self` mit `other` konkateniert.
|
||||||
|
|
||||||
|
`__radd__` hat genau das selbe verhalten, nur dass `other` mit `self` konkateniert wird.
|
||||||
|
|
||||||
|
```python
|
||||||
|
my_string = String("hello")
|
||||||
|
assert str(my_string + " world") == "hello world"
|
||||||
|
assert str("world " + my_str) == "world hello"
|
||||||
|
```
|
||||||
|
|
||||||
|
### `__len__`
|
||||||
|
|
||||||
|
Diese Methode soll uns die Länge unseres Strings zurückgeben
|
||||||
|
|
||||||
|
```python
|
||||||
|
assert len(String("hello world")) == 11
|
||||||
|
```
|
||||||
|
|
||||||
|
### `__eq__`
|
||||||
|
|
||||||
|
Diese Methode soll die Vergleichbarkeit zwischen *str* und *String* implementieren, also
|
||||||
|
|
||||||
|
```python
|
||||||
|
my_string = String("hello")
|
||||||
|
assert my_string == "hello"
|
||||||
|
assert my_string == String("hello")
|
||||||
|
```
|
||||||
|
|
||||||
|
### `__iter__`
|
||||||
|
|
||||||
|
Diese Methode soll einen Iterator aus unserem *String* machen damit wir über die einzelnen Characters iterieren können
|
||||||
|
|
||||||
|
```python
|
||||||
|
my_string = String("hello world")
|
||||||
|
my_it = iter(my_string)
|
||||||
|
for c in my_it:
|
||||||
|
print(c, end="") # hello world
|
||||||
|
```
|
||||||
|
|
||||||
|
## `concat`
|
||||||
|
|
||||||
|
Hierbei soll *self* nicht verändert werden, wir möchten *self* mit *other* konkatenieren und einen neuen konkatenierten *String* zurückgeben
|
||||||
|
|
||||||
|
## `contains`
|
||||||
|
|
||||||
|
Wir möchten schauen ob *other* in *self* enthalten ist
|
||||||
|
|
||||||
|
## `substring`
|
||||||
|
|
||||||
|
Wir möchten einen neuen *String* zurückgeben der alle Stellen von `start` bis `end`enthält. Hierbei sollten man von ausgehen dass `start` und `end` nicht negativ oder größer als der String sind.
|
||||||
|
|
||||||
|
## `strip`
|
||||||
|
|
||||||
|
Wir möchten am Anfang und Ende des Strings alle Stellen entfernen, solang diese in `chars` enthalten sind. Also
|
||||||
|
|
||||||
|
```python
|
||||||
|
assert String(" hello ").strip() == "hello"
|
||||||
|
```
|
||||||
|
|
||||||
|
## `replace`
|
||||||
|
|
||||||
|
Diese Methode soll überall `old` mit `new` genau `count`-Mal ersetzen. Wenn `count` negativ ist soll einfach jedes `old` ersetzt werden.
|
||||||
|
|
||||||
|
## `add_prefix`, `add_suffix`
|
||||||
|
|
||||||
|
`add_prefix` soll einen Prefix zu unserem String konkatenieren und analog dazu soll `add_suffix` einen Suffix zu unserem String konkatenieren
|
||||||
|
|
||||||
|
## `join`
|
||||||
|
|
||||||
|
Diese soll eine iterierbare Datenstruktur nehmen und zwischen jedes Element unseren *String* konkatenieren. Wir können davon ausgehen dass `T` zu einem *str* konvertiert werden kann (also `str(T)` anwendbar ist)
|
||||||
|
|
||||||
|
```python
|
||||||
|
assert String(", ").join([1, 2, 3, 4, 5]) == "1, 2, 3, 4, 5"
|
||||||
|
```
|
@@ -15,18 +15,24 @@ class String:
|
|||||||
|
|
||||||
def __add__(self, other: Str) -> 'String':
|
def __add__(self, other: Str) -> 'String':
|
||||||
return String(self.__s + str(other))
|
return String(self.__s + str(other))
|
||||||
|
|
||||||
def __iadd__(self, other: Str) -> 'String':
|
|
||||||
return self + other
|
|
||||||
|
|
||||||
|
def __radd__(self, other: Str) -> 'String':
|
||||||
|
return String(str(other) + self.__s)
|
||||||
|
|
||||||
def __len__(self) -> int:
|
def __len__(self) -> int:
|
||||||
return len(self.__s)
|
return len(self.__s)
|
||||||
|
|
||||||
def __eq__(self, other: Str) -> bool:
|
def __eq__(self, other: Str) -> bool:
|
||||||
return str(self) == str(other)
|
return str(self) == str(other)
|
||||||
|
|
||||||
|
def __iter__(self) -> Iterator[chr]:
|
||||||
|
pass
|
||||||
|
for i in range(0, len(self)):
|
||||||
|
yield chr(self.__s[i])
|
||||||
|
raise StopIteration
|
||||||
|
|
||||||
def concat(self, other: Str) -> None:
|
def concat(self, other: Str) -> 'String':
|
||||||
self + other
|
return self + other
|
||||||
|
|
||||||
def contains(self, other: Str) -> bool:
|
def contains(self, other: Str) -> bool:
|
||||||
s = str(other)
|
s = str(other)
|
||||||
@@ -38,9 +44,6 @@ class String:
|
|||||||
else:
|
else:
|
||||||
s = str(other)
|
s = str(other)
|
||||||
return len(s) == 0
|
return len(s) == 0
|
||||||
|
|
||||||
def concat(self, other: Str) -> None:
|
|
||||||
self.__s += str(other)
|
|
||||||
|
|
||||||
def substring(self, start: int, end: int) -> 'String':
|
def substring(self, start: int, end: int) -> 'String':
|
||||||
substr: str = ""
|
substr: str = ""
|
||||||
@@ -49,39 +52,41 @@ class String:
|
|||||||
substr += c
|
substr += c
|
||||||
return substr
|
return substr
|
||||||
|
|
||||||
def strip(self, chars: Str = ' ' + '\n' + '\t' + '\r') -> None:
|
def strip(self, chars: Str = ' ' + '\n' + '\t' + '\r') -> 'String':
|
||||||
i = 0
|
i = 0
|
||||||
while i < len(self) and self.__s[i] in chars:
|
while i < len(self) and self.__s[i] in chars:
|
||||||
i += 1
|
i += 1
|
||||||
j: int = len(self) - 1
|
j: int = len(self) - 1
|
||||||
while i <= j and self.__s[j] in chars:
|
while i <= j and self.__s[j] in chars:
|
||||||
j -= 1
|
j -= 1
|
||||||
self.__s: str = str(self)[i:j + 1]
|
return String(str(self)[i:j + 1])
|
||||||
|
|
||||||
def replace(self, old: Str, new: Str, count = -1) -> None:
|
def replace(self, old: Str, new: Str, count = -1) -> 'String':
|
||||||
o = str(old)
|
o = str(old)
|
||||||
n = str(new)
|
n = str(new)
|
||||||
|
new_str = self.__s
|
||||||
j = 0
|
j = 0
|
||||||
while count > 0 or (count < 0 and j < len(self)):
|
while count > 0 or (count < 0 and j < len(new_str)):
|
||||||
i: int = j
|
i: int = j
|
||||||
while len(o) > 0 and j < len(self):
|
while len(o) > 0 and j < len(new_str):
|
||||||
if o[0] == self.__s[j]:
|
if o[0] == new_str[j]:
|
||||||
o: str = o[1:]
|
o: str = o[1:]
|
||||||
j += 1
|
j += 1
|
||||||
else:
|
else:
|
||||||
j += 1
|
j += 1
|
||||||
break
|
break
|
||||||
if len(o) <= 0:
|
if len(o) <= 0:
|
||||||
self.__s = self.__s[:i] + n + self.__s[j:]
|
new_str = new_str[:i] + n + new_str[j:]
|
||||||
j += len(new) - len(old)
|
j += len(new) - len(old)
|
||||||
count -= 1
|
count -= 1
|
||||||
o = str(old)
|
o = str(old)
|
||||||
|
return new_str
|
||||||
|
|
||||||
def add_prefix(self, prefix: Str) -> None:
|
def add_prefix(self, prefix: Str) -> 'String':
|
||||||
self.__s = str(prefix) + self.__s
|
return prefix + self
|
||||||
|
|
||||||
def add_suffix(self, suffix: Str) -> None:
|
def add_suffix(self, suffix: Str) -> 'String':
|
||||||
self.__s += str(suffix)
|
return self + suffix
|
||||||
|
|
||||||
def join[T](self, xs: Iterator[T]) -> 'String':
|
def join[T](self, xs: Iterator[T]) -> 'String':
|
||||||
output = String("")
|
output = String("")
|
||||||
|
@@ -1,9 +1,53 @@
|
|||||||
from dataclasses import dataclass
|
from dataclasses import dataclass
|
||||||
|
from typing import Iterator
|
||||||
|
|
||||||
# kleine Hilfestellung mit `Str` kann man sowohl `str` als auch `String` meinen
|
# kleine Hilfestellung mit `Str` kann man sowohl `str` als auch `String` meinen
|
||||||
type Str = 'String' | str
|
type Str = 'String' | str
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
class String:
|
class String:
|
||||||
# Implement!
|
|
||||||
pass
|
def __post_init__(self, s: str) -> None:
|
||||||
|
pass
|
||||||
|
|
||||||
|
def __str__(self) -> str:
|
||||||
|
pass
|
||||||
|
|
||||||
|
def __add__(self, other: Str) -> 'String':
|
||||||
|
pass
|
||||||
|
|
||||||
|
def __radd__(self, other: Str) -> 'String':
|
||||||
|
pass
|
||||||
|
|
||||||
|
def __len__(self, other: Str) -> int:
|
||||||
|
pass
|
||||||
|
|
||||||
|
def __eq__(self, other: Str) -> bool:
|
||||||
|
pass
|
||||||
|
|
||||||
|
def __iter__(self) -> Iterator[chr]:
|
||||||
|
pass
|
||||||
|
|
||||||
|
def concat(self, other: Str) -> 'String':
|
||||||
|
pass
|
||||||
|
|
||||||
|
def contains(self, other: Str) -> bool:
|
||||||
|
pass
|
||||||
|
|
||||||
|
def substring(self, start: int, end: int) -> 'String':
|
||||||
|
pass
|
||||||
|
|
||||||
|
def strip(self, chars: Str = ' ' + '\n' + '\t' + '\r') -> 'String':
|
||||||
|
pass
|
||||||
|
|
||||||
|
def replace(self, old: Str, new: Str, count = -1) -> 'String':
|
||||||
|
pass
|
||||||
|
|
||||||
|
def add_prefix(self, prefix: Str) -> 'String':
|
||||||
|
pass
|
||||||
|
|
||||||
|
def add_suffix(self, suffix: Str) -> 'String':
|
||||||
|
pass
|
||||||
|
|
||||||
|
def join[T](self, xs: Iterator[T]) -> 'String':
|
||||||
|
pass
|
@@ -13,36 +13,33 @@ def test_strings_concat():
|
|||||||
test_str = String("")
|
test_str = String("")
|
||||||
test_str += "test succesful!"
|
test_str += "test succesful!"
|
||||||
assert test_str == "test succesful!"
|
assert test_str == "test succesful!"
|
||||||
test_str.concat(" Or is it?")
|
assert test_str.concat(" Or is it?") == "test succesful! Or is it?"
|
||||||
assert test_str == "test succesful! Or is it?"
|
|
||||||
|
|
||||||
def test_strings_strip():
|
def test_strings_strip():
|
||||||
test_str = String(" halo? \n")
|
test_str = String(" halo? \n")
|
||||||
test_str.strip()
|
test_str = test_str.strip()
|
||||||
assert test_str == "halo?"
|
assert test_str == "halo?"
|
||||||
test_str = String(" \n ")
|
test_str = String(" \n ")
|
||||||
test_str.strip()
|
test_str = test_str.strip()
|
||||||
assert test_str == ""
|
assert test_str == ""
|
||||||
|
|
||||||
|
|
||||||
def test_strings_replace():
|
def test_strings_replace():
|
||||||
test_str = String("har har har, try replacing thhis")
|
test_str = String("har har har, try replacing thhis")
|
||||||
test_str.replace('har ', 'ha')
|
assert test_str.replace('har ', 'ha') == "hahahar, try replacing thhis"
|
||||||
assert test_str == "hahahar, try replacing thhis"
|
test_str = test_str.replace('har ', 'ha')
|
||||||
test_str.replace('r', '', 1)
|
assert test_str.replace('r', '', 1) == "hahaha, try replacing thhis"
|
||||||
assert test_str == "hahaha, try replacing thhis"
|
test_str = test_str.replace('r', '', 1)
|
||||||
test_str.replace('hh', 'h')
|
assert test_str.replace('hh', 'h') == "hahaha, try replacing this"
|
||||||
assert test_str == "hahaha, try replacing this"
|
test_str = test_str.replace('hh', 'h')
|
||||||
test_str.replace('try replacing this', "replaced")
|
assert test_str.replace('try replacing this', "replaced") == "hahaha, replaced"
|
||||||
assert test_str == "hahaha, replaced"
|
|
||||||
|
|
||||||
|
|
||||||
def test_add_pre_suf():
|
def test_add_pre_suf():
|
||||||
test_str = String(" ")
|
test_str = String(" ")
|
||||||
test_str.add_suffix("suff")
|
assert test_str.add_suffix("suff") == " suff"
|
||||||
assert test_str == " suff"
|
assert test_str.add_prefix("pref") == "pref "
|
||||||
test_str.add_prefix("pref")
|
assert test_str.add_suffix("suff").add_prefix("pref") == "pref suff"
|
||||||
assert test_str == "pref suff"
|
|
||||||
|
|
||||||
def test_join():
|
def test_join():
|
||||||
assert String(", ").join([1, 2, 3, 4]) == "1, 2, 3, 4"
|
assert String(", ").join([1, 2, 3, 4]) == "1, 2, 3, 4"
|
||||||
|
Reference in New Issue
Block a user