Fractions Quiz

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

Summary

We wrote our first Python program and became masters in fractions. By the way, we also learned how a Python program is structured and how we can control processes in the program.
In the first chapter we learned what we need variables for, what types they can have and how we can generate random numbers. We also learned how to extend Python with the help of the import command in order to be able to use additional functions.

Fractions

Fractions are still considered very unpopular today. It is not that difficult and easy to learn and train with the fractions quiz that we will develop.
Fractions consist of an upper number, called the numerator, and a lower number, the denominator. The fraction is represented in the form a/b, where a is the numerator and b is the denominator.
In order to anticipate fractures, there are various operations that can be performed. Here are some basic concepts:
Addition and subtraction of fractions: To add or subtract fractions with the same denominator, one adds or subtracts the numerators and keeps the denominator unchanged. For example: 1/4 + 3/4 = 4/4 = 1. If the denominators are different, they must first be brought to the lowest common denominator (P/E ratio).
Multiplication of fractions: To multiply fractions, one multiplies the numerators with each other and the denominators with each other. The result is then displayed as a fraction. For example: (2/3) * (3/5) = 6/15 = 2/5.
Division of fractions: To divide fractions, invert the second fraction (the divisor) and multiply it by the first fraction (the dividend). The result is represented as a fraction. For example: (2/3) / (3/5) = (2/3) * (5/3) = 10/9.
Shortening fractions: A fraction can be shortened by dividing both the numerator and denominator by the same divisor. One divides by the greatest common divisor (GCD) until no further division is possible. For example: 8/12 can be shortened by dividing the numerator and denominator by 4 and gives 2/3.

We will now turn all these operations into a quiz program with the help of Python.

In the first program, we just wrote everything in the main function. This is also good to get you started. However, if we take a closer look at the program, we see that it actually consists of several parts. We have a part that determines the numbers, a part that determines the operation, and a part that does the input and verification.
If we divide a program into such parts, it will be clearer and easier to read. There are functions for this purpose. A function is a reusable block of code that performs a specific task. It is a fundamental concept in programming that helps organize and modularize code by breaking it down into smaller, self-contained units. Functions make the code more readable, maintainable, and efficient.

To define a function in Python, you usually use the def keyword, followed by the function name, parentheses for optional parameters, and a colon.
Here is a basic syntax for defining a function:

def function_name(parameter1, parameter2, …):

# code statements
# return statement (optional)

Now let’s look at the components of a function:

function_name: This is the name you give to your function, which follows the same naming conventions as the names of the variables.

Parameter:
These are optional placeholders that represent values that you want the function to receive when it is called. Parameters allow us to pass data to the function. Functions can have no or more parameters separated by commas.

Code Statements:
These are the actual statements or actions performed by the function. It can be any valid Python code, including control structures (if statements, loops, etc.), variable assignments, and function calls. However, it is important that the code that belongs to the function is indented.

return statement: This is an optional statement that specifies the value to be returned by the function. Using the return keyword followed by an expression, the function can send a result back to the caller. If the return statement is not used, the function returns None by default.
Here is an example of a simple function that calculates the sum of two numbers and returns the result:

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

And the call

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

This call must always happen after the function. So first the function, then the call.

So back to our fractions program:

In Python there is a library called fractions, which we import times and also our random library.

import random
from fractions import Fraction

The fraction library in Python provides a set of classes and functions for working with rational numbers (fractions). Rational numbers are numbers that can be expressed as the ratio of two integers, such as 1/2 or 3/4.

To make the code more beautiful, we want to use functions. For our fractions quiz we need a function that generates a fraction for us. In order not to make it too difficult, we want fractions from 1 to 10.
So our first function makes a random break:

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

First we generate the numerator, then the denominator. From both numbers we make a variable of type Fraction, this is done in the line:

zufallBruch = Fraction(zaehler, nenner)

We also give this break back!

If we look at our first program, we can also extract another function from our source. The examination of the result.
So what do we need to write the function. Once the two fractions and the mathematical operator with which we want to calculate the result.

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

Let’s take a closer look at this feature, as it contains two interesting pieces of code that are new to us.
On the one hand, raise ValueError(„Wrong operation“).
The raise ValueError („Wrong Operation“) statement in Python throws an exception of type ValueError and outputs the specified error message „Wrong Operation“.
The ValueError exception is typically used when a value is invalid or unexpected. Throwing this exception signals that an erroneous operation was performed or that there is an invalid value.
This also means that, if, as in our case, the variable operator contains, for example, „?“, the line raise ValueError stops processing and returns to the calling function. If it is not „caught“ there, the application will be terminated. But how do you „catch“ an exception. This is actually quite simple, there is the try/except construct. This construct can be used to handle exceptions.
For example:

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)

This is where the divide function is performed. Since dividing by 0 doesn’t work, we’ve built a ValueError Exception into the function.
Before we call the function, we write try: everything we want to try, we indent again.
After the commands, we write except and the exception we want to handle. Then indented again what should happen – in the above case we give an error message.
The second oddity is that we return two variables in this function. This has so, its correctness in Python is that possible. This is then called like this:
user_antwort_decimal, user_antwort_fraction = antwort_berechnen(fraction1, fraction2, operator)
But be careful, the order of the return values must be the same as that of the expected ones.
Now we can get back to our program and start with the actual main function:
def main():

It’s always nice to give out a welcome gift.

print("Willkommen beim Bruchrechnen!")

Next, we define a variable in which we store the correct answers and how many questions we want to make.

score = 0
num_Frage = 5

Furthermore, we need a variable in which we store all possible operators. In Python there is the possibility to declare so-called arrays.
Arrays are nothing more than a collection of data of the same type. So it can contain strings, numbers, etc.
With this knowledge, we can now declare an array of all operators.

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

Next, we need a For loop that asks as many questions as we defined in the num_Frage.

for _ in range(num_Frage):

Now let’s take a closer look:
for: The „for“ keyword initiates a loop construction and indicates that a loop is running.

The underscore symbol (_) is often used as a wildcard variable when the value of a variable in the loop is not needed. In this case, it is used to indicate that the value of the pass is not used and can be ignored.

in: The „in“ keyword is used to specify the range or iterable over which the loop should iterate.

range(num_Frage): The range() function produces a sequence of numbers ranging from 0 to num_Frage – 1. The argument is the number of repetitions that the loop should go through. For example, range(5) produces the numbers 0, 1, 2, 3, and 4.

Now we move in again and write our quiz. First, we determine the two fractions that we want to calculate. That’s what we’ve written a function for.

fraction1 = bruch()
  fraction2 = bruch()

So now we need an operator for our question. We do this with the following command:

operator = random.choice(operators)

We already know random, choice is now a function that randomly selects a value from a list. In our example, random.choice selects an operator from the operator array.

What’s missing: Ask the user.
So we output the question, we do this with the print command.

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

The print(f „What yields {Fraktion1} {Operator} {Fraktion2}?“) statement in Python uses f-strings (formatted strings) to dynamically format and display a string of variables and other values.
To illustrate this:
Let’s assume that fraction1 is 1/2, the operator is +, and fraction2 is 1/2. In the above case, the above command would be „What is 1/2 + 1/2?“. Without the f in front of the “ the above command would output „What yields {fraction1} {operator} {fraction2}?“. The f makes our lives easier and also makes the code easier to read.
Next, we are waiting for the user’s input. The input, as we already know from the first program, is a string and not of type fraction.
We therefore need to convert the user’s input. To do this, we make a separate function

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

In this function, we use the Try used earlier to check if the input was a fraction. The line fraction = Fraction(fraction_str), throws an exception if the string cannot be recognized as a fraction.
If it is not a valid input, then the function returns None. None is nothing more than nothing, null and is also used when the programmer wants the result to be still or not defined.
Now back to the program:

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.")

This is now our central part of our program.
First, we wait for the user to enter.
We now try to turn this input into a fraction with our function we just wrote. Now, if the user has made a valid input, i.e. the result is not None, we can check the result:

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

This is where we apply our antwort_berechnen. It should be noted here that the two return values, the return values of the function correspond to as a reminder: return decimal_answer, result .

Now all we have to do is compare the question with the answer and output the result!
Ready!
It wasn’t that hard. You can download the whole program at: gpiwonka/Fractions (github.com).