Compare commits

...

8 Commits

Author SHA1 Message Date
1efd3b66b0 added graphs 2025-02-06 15:34:01 +01:00
483c6ee562 Update README.md 2024-09-20 17:14:17 +02:00
6f733ce98e Update README.md 2024-09-20 10:10:43 +02:00
c83f33cfca Update recursion/recursive_datastructure/solution/lists.py 2024-02-17 17:02:47 +01:00
c7c1f3ea98 added BST, but remove not working 2024-02-15 04:40:13 +01:00
4c5d27e8d1 added some recursive datastructures 2024-02-14 18:44:59 +01:00
36b6a78833 strings 2024-02-14 01:14:59 +01:00
14cd9fe8b0 added README.md for List-Comprehension exercise 2024-02-11 00:20:19 +01:00
19 changed files with 789 additions and 45 deletions

View File

@@ -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
@@ -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
- Könnt auch gerne nachfragen wenn was nicht funktioniert!
- Dann könnt ihr einfach die Tests mit `pytest` in der Konsole aufrufen
- Schlagen die Tests fehl sieht das so aus:
![image not found](https://cloud.narl.io/s/8Dj4E79RKnHZQNJ/preview)
- Hier sagt euch Pytest auch was alles nicht an eurem Code funktioniert
- Funktioniert euer Code sieht das so aus:
![image not found](https://cloud.narl.io/s/2HGdaiQkP4YEQ5K/preview)
## Kontakt

View 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:
![image not found](image.png)
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'],
})
```

View 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

Binary file not shown.

After

Width:  |  Height:  |  Size: 89 KiB

View File

@@ -1 +1,56 @@
# 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]
```
---

View 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`.

View 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

View 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

View 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

View 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

View 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

View File

@@ -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))

View 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"

View 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

View 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

View File

@@ -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.
## 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"
```

View File

@@ -16,8 +16,8 @@ class String:
def __add__(self, other: Str) -> 'String':
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:
return len(self.__s)
@@ -25,8 +25,14 @@ class String:
def __eq__(self, other: Str) -> bool:
return str(self) == str(other)
def concat(self, other: Str) -> None:
self + 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) -> 'String':
return self + other
def contains(self, other: Str) -> bool:
s = str(other)
@@ -39,9 +45,6 @@ class String:
s = str(other)
return len(s) == 0
def concat(self, other: Str) -> None:
self.__s += str(other)
def substring(self, start: int, end: int) -> 'String':
substr: str = ""
for i, c in enumerate(self.__s):
@@ -49,39 +52,41 @@ class String:
substr += c
return substr
def strip(self, chars: Str = ' ' + '\n' + '\t' + '\r') -> None:
def strip(self, chars: Str = ' ' + '\n' + '\t' + '\r') -> 'String':
i = 0
while i < len(self) and self.__s[i] in chars:
i += 1
j: int = len(self) - 1
while i <= j and self.__s[j] in chars:
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)
n = str(new)
new_str = self.__s
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
while len(o) > 0 and j < len(self):
if o[0] == self.__s[j]:
while len(o) > 0 and j < len(new_str):
if o[0] == new_str[j]:
o: str = o[1:]
j += 1
else:
j += 1
break
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)
count -= 1
o = str(old)
return new_str
def add_prefix(self, prefix: Str) -> None:
self.__s = str(prefix) + self.__s
def add_prefix(self, prefix: Str) -> 'String':
return prefix + self
def add_suffix(self, suffix: Str) -> None:
self.__s += str(suffix)
def add_suffix(self, suffix: Str) -> 'String':
return self + suffix
def join[T](self, xs: Iterator[T]) -> 'String':
output = String("")

View File

@@ -1,9 +1,53 @@
from dataclasses import dataclass
from typing import Iterator
# kleine Hilfestellung mit `Str` kann man sowohl `str` als auch `String` meinen
type Str = 'String' | str
@dataclass
class String:
# Implement!
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

View File

@@ -13,36 +13,33 @@ def test_strings_concat():
test_str = String("")
test_str += "test succesful!"
assert test_str == "test succesful!"
test_str.concat(" Or is it?")
assert test_str == "test succesful! Or is it?"
assert test_str.concat(" Or is it?") == "test succesful! Or is it?"
def test_strings_strip():
test_str = String(" halo? \n")
test_str.strip()
test_str = test_str.strip()
assert test_str == "halo?"
test_str = String(" \n ")
test_str.strip()
test_str = test_str.strip()
assert test_str == ""
def test_strings_replace():
test_str = String("har har har, try replacing thhis")
test_str.replace('har ', 'ha')
assert test_str == "hahahar, try replacing thhis"
test_str.replace('r', '', 1)
assert test_str == "hahaha, try replacing thhis"
test_str.replace('hh', 'h')
assert test_str == "hahaha, try replacing this"
test_str.replace('try replacing this', "replaced")
assert test_str == "hahaha, replaced"
assert test_str.replace('har ', 'ha') == "hahahar, try replacing thhis"
test_str = test_str.replace('har ', 'ha')
assert test_str.replace('r', '', 1) == "hahaha, try replacing thhis"
test_str = test_str.replace('r', '', 1)
assert test_str.replace('hh', 'h') == "hahaha, try replacing this"
test_str = test_str.replace('hh', 'h')
assert test_str.replace('try replacing this', "replaced") == "hahaha, replaced"
def test_add_pre_suf():
test_str = String(" ")
test_str.add_suffix("suff")
assert test_str == " suff"
test_str.add_prefix("pref")
assert test_str == "pref suff"
assert test_str.add_suffix("suff") == " suff"
assert test_str.add_prefix("pref") == "pref "
assert test_str.add_suffix("suff").add_prefix("pref") == "pref suff"
def test_join():
assert String(", ").join([1, 2, 3, 4]) == "1, 2, 3, 4"