Bruchrechnen Quiz

Sketch of math equation on blackboard. Fractions learning and examples. Handwritten on chalkboard

Zusammenfassung

Wir haben unser erstes Python Programm geschrieben und sind Meister im Bruchrechnen geworden. So nebenbei haben wir auch gelernt wie ein Python Programm strukturiert ist und wie wir Abläufe im Programm steuern können.
Im ersten Kapitel haben wir gelernt für was wir Variablen brauchen, welche Typen diese haben können und wie wir Zufallszahlen genieren können. Auch haben wir gelernt wie wir Python mit Hilfe des Import Befehls erweitern können, um zusätzliche Funktionen nutzen zu können.

Bruchrechnen

Bruchrechnen gilt heutzutage noch immer als sehr unbeliebt. Dabei ist es gar nicht mal so schwer und mit dem Bruchrechnen Quiz, den wir entwickeln werden, leicht zu erlernen und zu trainieren.
Brüche bestehen aus einer oberen Zahl, der sogenannten Zähler, und einer unteren Zahl, dem Nenner. Der Bruch wird in der Form a/b dargestellt, wobei a der Zähler und b der Nenner ist.
Um mit Brüchen zu rechnen, gibt es verschiedene Operationen, die man durchführen kann. Hier sind einige grundlegende Konzepte:
Addition und Subtraktion von Brüchen: Um Brüche mit demselben Nenner zu addieren oder zu subtrahieren, addiert bzw. subtrahiert man die Zähler und behält den Nenner unverändert. Zum Beispiel: 1/4 + 3/4 = 4/4 = 1. Wenn die Nenner unterschiedlich sind, müssen sie zunächst auf den kleinsten gemeinsamen Nenner (kgV) gebracht werden.
Multiplikation von Brüchen: Um Brüche zu multiplizieren, multipliziert man die Zähler miteinander und die Nenner miteinander. Das Ergebnis wird dann als Bruch dargestellt. Zum Beispiel: (2/3) * (3/5) = 6/15 = 2/5.
Division von Brüchen: Um Brüche zu dividieren, invertiert man den zweiten Bruch (den Divisor) und multipliziert ihn mit dem ersten Bruch (dem Dividend). Das Ergebnis wird als Bruch dargestellt. Zum Beispiel: (2/3) / (3/5) = (2/3) * (5/3) = 10/9.
Kürzen von Brüchen: Ein Bruch kann gekürzt werden, indem sowohl Zähler als auch Nenner durch denselben Teiler dividiert werden. Man teilt so lange durch den größten gemeinsamen Teiler (ggT), bis keine weitere Division möglich ist. Zum Beispiel: 8/12 kann durch Teilen von Zähler und Nenner durch 4 gekürzt werden und ergibt 2/3.

Alle diese Operationen werden wir nun mit Hilfe von Python in ein Quiz Programm verwandeln.

Im ersten Programm haben wir einfach alles in der Main Funktion geschrieben. Dies ist zum Einstieg auch gut. Jedoch wenn wir das Programm genauer anschauen, sehen wir, das es eigentlich aus mehreren Teilen besteht. Wir haben einen Teil, der die Zahlen ermittelt, einen Teil der die Operation bestimmt und einen Teil der die Eingabe und Überprüfung übernimmt.
Wenn wir ein Programm in solche Teile aufteilen, wird es übersichtlicher und leichter zu lesen. Zu diesem Zweck gibt es Funktionen. Eine Funktion ist ein wieder verwendbarer Codeblock, der eine bestimmte Aufgabe ausführt. Sie ist ein grundlegendes Konzept in der Programmierung, das hilft, Code zu organisieren und zu modularisieren, indem es ihn in kleinere, in sich geschlossene Einheiten aufteilt. Funktionen machen den Code lesbarer, wartbarer und effizienter.

Um eine Funktion in Python zu definieren, verwenden Sie normalerweise das Schlüsselwort def, gefolgt vom Funktionsnamen, Klammern für optionale Parameter und einem Doppelpunkt.
Hier ist eine grundlegende Syntax für die Definition einer Funktion:

def function_name(parameter1, parameter2, …):

# code statements
# return statement (optional)

Schauen wir uns nun die Bestandteile einer Funktion an:

function_name: Dies ist der Name, den Sie Ihrer Funktion geben und der den gleichen Namenskonventionen folgt wie die Namen der Variablen.

Parameter:
Hierbei handelt es sich um optionale Platzhalter, die Werte darstellen, die die Funktion beim Aufruf erhalten soll. Mit Parametern können wir Daten an die Funktion übergeben. Funktionen können keine oder mehr Parameter haben, die durch Kommas getrennt sind.

Code-Anweisungen:
Dies sind die eigentlichen Anweisungen oder Aktionen, die von der Funktion ausgeführt werden. Es kann sich um jeden gültigen Python-Code handeln, einschließlich Kontrollstrukturen (if-Anweisungen, Schleifen usw.), Variablenzuweisungen und Funktionsaufrufe. Wichtig ist jedoch das der Code, der zu der Funktion gehört eingerückt ist.

return-Anweisung: Dies ist eine optionale Anweisung, die den Wert angibt, der von der Funktion zurückgegeben werden soll. Mit dem Schlüsselwort return, gefolgt von einem Ausdruck, kann die Funktion ein Ergebnis an den Aufrufer zurücksenden. Wenn die return-Anweisung nicht verwendet wird, gibt die Funktion standardmäßig None zurück.
Hier ein Beispiel für eine einfache Funktion, die die Summe von zwei Zahlen berechnet und das Ergebnis zurück gibt:

def add_numbers(a, b):
sum = a + b
return sum

Und der Aufruf

result = add_numbers(5, 3)
print(result) # Ausgabe: 8

Dieser Aufruf muss immer nach der Funktion passieren. Also zuerst die Funktion danach der Aufruf.

So zurück zu unserem Bruchrechnungs Programm:

In Python gibt es eine Bibliothek namens fractions, die importieren wir mal und auch unser random Bibliothek.

import random
from fractions import Fraction

Die Fraktionsbibliothek in Python bietet eine Reihe von Klassen und Funktionen für die Arbeit mit rationalen Zahlen (Brüchen). Rationale Zahlen sind Zahlen, die als Verhältnis zweier ganzer Zahlen ausgedrückt werden können, z. B. 1/2 oder 3/4.

Um den Code schöner zu machen, wollen wir ja Funktionen verwenden. Für unser Bruchrechnen Quiz brauchen wir eine Funktion die uns einen Bruch generiert. Um es nicht zu schwierig zu machen, wollen wir Brüche von 1 bis 10.
Unsere erste Funktion macht also einen zufälligen Bruch:

def bruch():
    zaehler = random.randint(1, 10)
    nenner = random.randint(zaehler + 1, 10)
    zufallBruch = Fraction(zaehler, nenner)
    return zufallBruch

Zuerst generieren wir den Zähler, dann den Nenner. Aus beiden Zahlen machen wir eine Variable vom Typ Fraction, dies geschieht in der Zeile:

zufallBruch = Fraction(zaehler, nenner)

Diesen Bruch geben wir auch zurück!

Wenn wir uns unser erstes Programm anschauen, dann können wir auch noch eine andere Funktion aus unserem Source extrahieren. Die Prüfung des Ergebnisses.
Also was brauchen wir, um die Funktion zu schreiben. Einmal die zwei Brüche und den mathematischen Operator, mit dem wir das Ergebnis berechnen wollen.

def antwort_berechnen(fraction1, fraction2, operator):
    if operator == '+':
       result = fraction1 + fraction2
    elif operator == '-':
         result = fraction1 - fraction2
    elif operator == '*':
         result = fraction1 * fraction2
    elif operator == '/':
          result = fraction1 / fraction2
    else:
       raise ValueError("Falsche Operation")

    decimal_answer = result.numerator / result.denominator
    return decimal_answer, result

Schauen wir uns diese Funktion genauer an, da sie zwei interessante Code Teile, die für uns neu sind enthält.
Zum einen raise ValueError(“Falsche Operation”).
Die Anweisung raise ValueError(„Falsche Operation“) in Python erzeugt eine Ausnahme (Exception) vom Typ ValueError und gibt dabei die angegebene Fehlermeldung „Falsche Operation“ aus.
Die ValueError-Ausnahme wird normalerweise verwendet, wenn ein Wert ungültig oder unerwartet ist. Durch das Auslösen dieser Ausnahme wird signalisiert, dass eine fehlerhafte Operation ausgeführt wurde oder ein ungültiger Wert vorliegt.
Das heißt auch das, wenn, wie in unserem Fall die Variable operator zum Beispiel “?” enthält, in der Zeile raise ValueError(“Falsche Operation”) die Abarbeitung stoppt und kehrt in die aufrufende Funktion zurück. Sollte sie dort nicht “gefangen” werden, wird die Applikation beendet. Doch wie “fängt” man eine Exception. Das ist eigentlich ganz einfach dazu gibt es das Try/except Konstrukt. Mit diesem Konstrukt können Exception bearbeitet werden.
Ein Beispiel:

def divide(a, b):
     if b == 0:
     raise ValueError("Falsche Operation: Division durch Null ist nicht erlaubt.")
     return a / b

try:
    result = divide(10, 0)
    print(result)
except ValueError as error:
    print("Ein Fehler ist aufgetreten:", error)

Hier wird nun die Funktion divide aufgreufen. Da eine Division durch 0 nicht funktioniert, haben wir eine ValueError Exception in die Funktion eingebaut.
Bevor wir nun die Funktion aufrufen schreiben wir try: alles was wir ausprobieren wollen, rücken wir wieder ein.
Nach den Befehlen, schreiben wir except und die Exception die wir verarbeiten wollen. Dann wieder eingerückt was passieren soll – in obigen Fall geben wir eine Fehlermeldung aus.
Die zweite Seltsamkeit ist, das wir in dieser Funktion zwei Variablen zurückgeben. Das hat so, seine Richtigkeit in Python ist das möglich. Aufgerufen wird das dann so:
user_antwort_decimal, user_antwort_fraction = antwort_berechnen(fraction1, fraction2, operator)
Doch Achtung die Reihenfolge der Rückgabewerte muss gleich sein, wie die der erwarteten.
Jetzt können wir uns wir uns wieder unserem Programm widmen und mit der eigentlichen Main Funktion anfangen:
def main():

Es ist immer nett einen Willkommensgruß auszugeben.

print("Willkommen beim Bruchrechnen!")

Als nächstes definieren wir eine Variable, in der wir die richtigen Antworten speichern und wie viele Fragen wir machen wollen.

score = 0
num_Frage = 5

Weiters brauchen wir eine Variable, in der wir alle möglichen Operatoren speichert. In Python gibt es die Möglichkeit sogenannte Arrays zu deklarieren.
Arrays sind nichts anderes, als eine Sammlung von Daten des selben Typs. Es kann also Strings, Zahlen usw. Enthalten.
Mit diesem Wissen können wir nun ein Array aller Operatoren deklarieren.

operators = ['+', '-', '*', '/']

Als nächstes brauchen wir eine For Schleife, die so viele Fragen stellt, wie wir im num_Frage definiert haben.

for _ in range(num_Frage):

Nun schauen wir uns das genauer an:
for: Das Schlüsselwort „for“ leitet eine Schleifenkonstruktion ein und gibt an, dass eine Schleife ausgeführt wird.

Das Unterstrich-Symbol (_) wird oft als Platzhaltervariable verwendet, wenn der Wert einer Variablen in der Schleife nicht benötigt wird. In diesem Fall wird es verwendet, um anzuzeigen, dass der Wert des Durchlaufs nicht verwendet wird und ignoriert werden kann.

in: Das Schlüsselwort „in“ wird verwendet, um den Bereich oder das Iterable anzugeben, über das die Schleife iterieren soll.

range(num_Frage): Die Funktion range() erzeugt eine Sequenz von Zahlen, die von 0 bis num_Frage – 1 reicht. Es wird als Argument die Anzahl der Wiederholungen angegeben, die die Schleife durchlaufen soll. Zum Beispiel erzeugt range(5) die Zahlen 0, 1, 2, 3 und 4.

Nun rücken wir wieder ein und schreiben unser Quiz. Als erstes bestimmen wir die zwei Brüche, die wir berechnen lassen wollen, erstellen. Dafür haben wir ja eine Funktion geschrieben.

fraction1 = bruch()
  fraction2 = bruch()

So nun brauchen wir noch einen Operator für unsere Frage. Dies bewerkstelligen wir mit folgendem Befehl:

operator = random.choice(operators)

random kennen wir ja schon, choice ist nun eine Funktion, die aus einer Liste zufällig einen Wert auswählt. In unserem Beispiel wählt random.choice aus dem Array operator einen Operator aus.

Was fehlt noch: Den User fragen.
Also geben wir die Frage aus, dies machen wir mit dem print Befehl.

print(f"Was ergibt {fraction1} {operator} {fraction2}?")

Die Anweisung print(f „Was ergibt {Fraktion1} {Operator} {Fraktion2}?“) in Python verwendet f-Strings (formatierte Zeichenketten), um eine Zeichenkette mit Variablen und anderen Werten dynamisch zu formatieren und anzuzeigen.
Um das zu veranschaulichen:
Nehmen wir an, das fraction1 1/2, der Operator + und fraction2 1/2 ist. Im obigen Fall würde der obige Befehl “Was ergibt 1/2 + 1/2?”. Ohne das f vor dem “ würde der obige Befehl „Was ergibt {fraction1} {operator} {fraction2}?“ ausgeben. Das f erleichtert uns das Leben und macht den Code auch leichter lesbar.
Als nächstes warten wir auf die Eingabe des Benutzers. Die Eingabe, wie wir schon aus dem ersten Programm wissen, ist ja ein String und nicht des Typs fraction.
Wir müssen daher die Eingabe des Users umwandeln. Dazu machen wir eine eigene Funktion

def parse_fraction(fraction_str):
    try:
       fraction = Fraction(fraction_str)
       return fraction
    except ValueError:
        return None

In dieser Funktion verwenden wir das vorhin verwendete Try um zu überprüfen, ob die Eingabe ein Bruch war. Die Zeile fraction = Fraction(fraction_str), wirft eine Exception, wenn der String nicht als Bruch erkannt werden kann.
Wenn es keine gültige Eingabe ist dann returniert die Funktion None. None ist nichts anderes als nix, null und wird auch verwendet, wenn der Programmierer will das das Ergebnis noch oder nicht definiert ist.
Nun zurück zum Programm:

user_input = input("Deine Antwort: ")
user_fraction = parse_fraction(user_input)
if user_fraction is not None:
    user_antwort_decimal, user_antwort_fraction = antwort_berechnen(fraction1, fraction2, operator)
    if user_fraction == user_antwort_fraction:
       print("Super! Richtig!")
       score += 1
    else:
        print(f"Falsch! Die richtige Antwort ist {user_antwort_decimal} oder {user_antwort_fraction}.")
else:
print("Ungueltige Eingabe. Bitte geben Sie eine gueltige Bruch- oder Dezimalzahl ein.")

Dies ist nun unser zentraler Teil unseres Programms.
Zuerst warten wir auf die Eingabe des Users.
Diese Eingabe versuchen wir nun, mit unserer soeben geschriebenen Funktion, in einen Bruch zu verwandeln. Wenn der Benutzer nun eine gültige Eingabe gemacht hat, also das Ergebnis nicht None ist, können wir das Ergebnis überprüfen:

user_antwort_decimal, user_antwort_fraction = antwort_berechnen(fraction1, fraction2, operator)

Hier wenden wir nun unsere antwort_berechnen an. Hier ist eben zu beachten, das die zwei Rückgabewerte, der Return Werten der Funktion entspricht zur Erinnerung: return decimal_answer, result .

Nun müssen wir nur noch die Frage mit der Antwort vergleichen und das Ergebnis ausgeben!
Fertig!
War ja nicht so schwer. Das ganze Programm kannst Du unter: gpiwonka/Bruchrechnen (github.com) herunterladen.