diff --git a/Tutorium/tut05/README.md b/Tutorium/tut05/README.md index 6fe3d16..4d2c074 100644 --- a/Tutorium/tut05/README.md +++ b/Tutorium/tut05/README.md @@ -40,9 +40,7 @@ ## Übungsaufgaben -### Annotations - -#### [Primes](./src/primes.py) +### [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 @@ -51,6 +49,77 @@ def prime_factorization(n): pass ``` -#### [Dataclass](./src/data_classes.py) +### [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 +``` diff --git a/Tutorium/tut05/src/data_classes.py b/Tutorium/tut05/src/data_classes.py new file mode 100644 index 0000000..816f1ba --- /dev/null +++ b/Tutorium/tut05/src/data_classes.py @@ -0,0 +1,88 @@ +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}"