This commit is contained in:
2024-01-19 04:41:07 +01:00
parent 12feb4c1c5
commit 5e531ce0c6
3 changed files with 290 additions and 26 deletions

View File

@ -14,6 +14,213 @@ Musterlösung 11 - Wiederholung Types - Functions!
--- ---
# Musterlösung - Exercise 11
---
# Aufgabe 11.1 - Generatoren; `generators.py` [`10p`]
---
## Aufgabe 11.1 a - collatz; [`2.5p`]
Es seien $i \in \mathbb{N}_0$ und $n \in \mathbb{N}$, so ist die Collatz-Folge definiert als
$$
\begin{align*}
c_0 &= n \\
c_{i+1} &=
\begin{cases}
\frac{c_i}{2}, &c_i\mod 2 = 0 \\
3 \cdot c_i + 1, &c_i\mod 2 = 1
\end{cases}
\end{align*}
$$
Dabei gilt $c_i = 1$ als Abbruchbedingung des Generators
---
## Aufgabe 11.1 a - collatz; [`2.5p`]
```python
def collatz(n: int) -> Generator[int, None, None]:
if n < 1:
return
while n > 1:
yield n
if n % 2 == 0:
n = n // 2
else:
n = 3 * n + 1
yield n
```
---
## Aufgabe 11.1 b - random; [`2.5p`]
---
## Aufgabe 11.1 b - random; [`2.5p`]
```python
def random(seed: int, a: int, b: int, m: int) -> Iterator[int]:
yi = seed
while True:
yield yi
yi = (a * yi + b) % m
```
---
## Aufgabe 11.1 c - chunks; [`2.5p`]
---
## Aufgabe 11.1 c - chunks; [`2.5p`]
```python
def chunks[T](iter: Iterator[T], n: int) -> Iterator[list[T]]:
while True:
xs = []
try:
for _ in range(n):
xs.append(next(iter))
yield xs
except StopIteration:
if xs:
yield xs
break
```
---
## Aufgabe 11.1 d - flatten; [`2.5p`]
---
## Aufgabe 11.1 d - flatten; [`2.5p`]
```python
def flatten[T](iters: Iterator[list[T]]) -> Iterator[T]:
for iter in iters:
yield from iter
```
---
# Aufgabe 11.2 - Graphen; `graphs.py` [`10p`]
Typaliase als Hilfestellung
```python
type GDict[T] = dict[T, set[T]]
type Graph[T] = GDict[T]
```
---
## Aufgabe 11.2 a - is_graph; [`2.5p`]
---
## Aufgabe 11.2 a - is_graph; [`2.5p`]
```python
def is_graph(d: GDict[Any]) -> bool:
for vals in d.values():
for val in vals:
if val not in d.keys():
return False
return True
```
---
## Aufgabe 11.2 b - to_graph; [`2.5p`]
---
## Aufgabe 11.2 b - to_graph; [`2.5p`]
```python
def to_graph[T](d: GDict[T]) -> Graph[T]:
res = dict()
for k, vals in d.items():
for val in vals:
if val not in d:
res[val] = set()
res[k] = vals
return res
```
---
## Aufgabe 11.2 c - nodes, edges; [`2.5p`]
---
## Aufgabe 11.2 c - nodes, edges; [`2.5p`]
```python
def edges[T](graph: Graph[T]) -> Iterator[tuple[T, T]]:
for key, value in graph.items():
for v in value:
yield (key, v)
def nodes[T](graph: Graph[T]) -> Iterator[T]:
yield from graph.keys()
```
---
## Aufgabe 11.2 d - invert_graph; [`2.5p`]
---
## Aufgabe 11.2 d - invert_graph; [`2.5p`]
```python
def invert_graph[T](graph: Graph[T]) -> Graph[T]:
res = dict()
for n in nodes(graph):
res[n] = set()
for a, b in edges(graph):
res[b].add(a)
return res
```
---
## Aufgabe 11.2 e - has_cycle; [`0p`]
---
## Aufgabe 11.2 e - has_cycle; [`0p`]
```python
def find_cycle[T](graph: Graph[T], start: T, visited: set[T]) -> bool:
assert start in graph
if start in visited:
return True
for value in graph[start]:
if find_cycle(graph, value, visited | {start}):
return True
return False
def has_cycle(graph: Graph[Any]) -> bool:
return any(find_cycle(graph, node, set()) for node in graph)
```
---
## Aufgabe 11.3 - Erfahrungen `NOTES.md`; [`0p`]
### Tragt eure Stunden ein!
---
# Type annotations # Type annotations
(Wiederholung) (Wiederholung)
@ -279,7 +486,7 @@ print(add_but_variable(3, 2)) # 5
- Verketten von Funktionen - Verketten von Funktionen
```python ```python
def compose[T](*funcs: Callable[[T], T]) -> Callable[[T], T]: def compose[T](*funcs: Callable[[T], T]) -> Callable[[T], T]:
return reduce(lambda f, g: lambda n: f(g(n)), funcs) return fold(lambda f, g: lambda n: f(g(n)), funcs)
f: Callable[[int], int] = lambda n: n + 42 f: Callable[[int], int] = lambda n: n + 42
g: Callable[[int], int] = lambda n: n ** 2 g: Callable[[int], int] = lambda n: n ** 2
@ -295,7 +502,7 @@ print(add_but_variable(3, 2)) # 5
- nehmen eine oder mehrere `Callable` als Argument - nehmen eine oder mehrere `Callable` als Argument
- geben ein `Callable` zurück - geben ein `Callable` zurück
### Higher-Order-Functions - `map` ### Higher-Order-Function - `map`
- Wendet ein `Callable` auf jedes Element in einem `Iterable` an - Wendet ein `Callable` auf jedes Element in einem `Iterable` an
@ -309,11 +516,68 @@ print(add_but_variable(3, 2)) # 5
--- ---
### Higher-Order-Functions - `filter` ### Higher-Order-Function - `filter`
- `filter` verarbeitet Datenstrukturen anhand eines Prädikats (`Callable`) - `filter` verarbeitet Datenstrukturen anhand eines Prädikats (`Callable`)
- behält nur Elemente die das Prädikat erfüllen - behält nur Elemente die das Prädikat erfüllen
```python ```python
def filter[T](predicate: Callable[[T], bool], xs: Iterable[T]) -> Iterable[T]: def filter[T](predicate: Callable[[T], bool], xs: Iterable[T]) -> Iterable[T]:
return [x for x in xs if predicate(x)] return [x for x in xs if predicate(x)]
```
predicate: Callable[[int | None] bool] = lambda e: bool(e)
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?

Binary file not shown.

View File

@ -9,7 +9,7 @@ def filter[T](predicate: Callable[[T], bool], xs: Iterable[T]) -> Iterable[T]:
return [x for x in xs if predicate(x)] return [x for x in xs if predicate(x)]
def reduce[T](func: Callable[[T, T], T], xs: Iterable[T]) -> T: def fold[T](func: Callable[[T, T], T], xs: Iterable[T]) -> T:
it: Iterator[T] = iter(xs) it: Iterator[T] = iter(xs)
value: T | None = None value: T | None = None
for x in it: for x in it:
@ -19,20 +19,20 @@ def reduce[T](func: Callable[[T, T], T], xs: Iterable[T]) -> T:
case _: case _:
value = func(value, x) value = func(value, x)
if not value: if not value:
raise TypeError("can't reduce empty list") raise TypeError("can't fold empty list")
return value return value
def flatten(xs: Iterable[Any]) -> Iterable[Any]: def flatten(xs: Iterable[Any]) -> Iterable[Any]:
new_list = [] new_list = []
for s in xs: for s in xs:
if isinstance(s, Iterable): if isinstance(s, Iterable):
new_list.append(flatten(s)) new_list += flatten(s)
else: else:
new_list.append([s]) new_list.append(s)
return new_list return new_list
def compose[T](*funcs: Callable[[T], T]) -> Callable[[T], T]: def compose[T](*funcs: Callable[[T], T]) -> Callable[[T], T]:
return reduce(lambda f, g: lambda n: f(g(n)), funcs) return fold(lambda f, g: lambda n: f(g(n)), funcs)
def poly(x: float) -> Callable[[float, float], Callable[[float], float]]: def poly(x: float) -> Callable[[float, float], Callable[[float], float]]:
@ -45,39 +45,39 @@ def main():
fhg: Callable[[int], int] = compose(f, g, h) fhg: Callable[[int], int] = compose(f, g, h)
# f(g(h(0))) <=> ((0 - 3) ** 2) + 42 = 52 # f(g(h(0))) <=> ((0 - 3) ** 2) + 42 = 51
print(fhg(0)) assert (tmp := fhg(0)) == 51
print(compose(f, g, h)(0)) assert compose(f, g, h)(0) == 51
assert list(filter(lambda e: bool(e), [1, 2, 3, None, 5, 6])) == [1, 2, 3, 5, 6]
assert list(filter(lambda e: not bool(e), [1, 2, 3, None, 5, 6])) == [None]
print(list(filter(lambda e: bool(e), [1, 2, 3, None, 5, 6]))) assert list(map(lambda e: str(e), [1, 2, 3, 4, 5, 6, "hello_functional"])) == ["1", "2", "3", "4", "5", "6", "hello_functional"]
print(list(filter(lambda e: not bool(e), [1, 2, 3, None, 5, 6])))
assert list(
print(list(map(lambda e: str(e), [1, 2, 3, 4, 5, 6, "hello_functional"])))
print(list(
filter(lambda e: len(e) > 1, filter(lambda e: len(e) > 1,
map(lambda e: str(e), map(lambda e: str(e),
[1, 2, 3, 4, "hello_world"])))) [1, 2, 3, 4, "hello_world"]))) == ["hello_world"]
print(list(filter(lambda e: isinstance(e, int), [1, 2, 3, "hello"]))) assert list(filter(lambda e: isinstance(e, int), [1, 2, 3, "hello"])) == [1, 2, 3]
print(list(flatten([[1, 2, 3], 4, [[5, 6], 7, [8, 9]]]))) 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: def add(a: int, b: int) -> int:
return a + b return a + b
add_but_variable: Callable[[int, int], int] = add add_but_variable: Callable[[int, int], int] = add
print(add_but_variable(3, 2)) # 5 assert add_but_variable(3, 2) == 5
add2: Callable[[int, int], int] = lambda x, y: x + y add2: Callable[[int, int], int] = lambda x, y: x + y
print(add2(2, 3)) assert add2(2, 3) == 5
print((lambda x, y: x + y)(3, 4)) assert (lambda x, y: x + y)(3, 4) == 7
print(reduce(lambda x, y: x + y, [1, 2, 3, 4])) # 10 sum: Callable[[Iterable[int]], int] = lambda xs: fold(lambda x, y: x + y, xs)
assert sum([1, 2, 3, 4]) == 10
print(poly(3)(2, 3)(5)) assert poly(3)(2, 3)(5) == 2 * 3 ** 2 + 3 * 3 + 5
if __name__ == '__main__': if __name__ == '__main__':
main() main()