Die deutsche Grammatik

Die deutsche Sprache ist bekannt für ihre Grammatik, von vielen – nicht nur Nicht-Muttersprachlern – gefürchtet.
Es gibt einige Regeln und Konventionen, die die Bildung von Wörtern, Sätzen und Texten erst zu einem Erlebnis machen.
An erster Stelle gibt es die Artikel. Im Gegensatz zum Englischen gibt es im Deutschen grundlegend drei: der, die, das. Dies sind die bestimmten Artikel. Daneben gibt es unbestimmte und Nullartikel, um das Geschlecht, den Numerus (Singular oder Plural) und den Kasus (Nominativ, Genitiv, Dativ, Akkusativ) von Substantiven anzuzeigen.
Zum Beispiel: „der Tisch“ (der bestimmte Artikel im Nominativ), „ein Buch“ (der unbestimmte Artikel im Akkusativ). Eine Beispiel für die Verwendung des Nullartikels ist: “Ich wohne in Österreich.”
Substantive sind Hauptwörter, die Personen, Orte, Dinge oder Konzepte bezeichnen. Sie haben ein grammatisches Geschlecht (männlich, weiblich, sächlich) und können im Singular oder Plural stehen.
Adjektive beschreiben Substantive und passen sich in Geschlecht, Numerus und Kasus an das zugehörige Substantiv an. Zum Beispiel: „großer Tisch“ (männlich, Nominativ), „große Blumen“ (weiblich, Nominativ).
Verben sind Handlungs- oder Tätigkeitswörter und bilden die Grundlage für Sätze. Sie können verschiedene Zeiten (Präsens, Präteritum, Perfekt, Futur), Modi (Indikativ, Konjunktiv) und Personen (ich, du, er/sie/es) haben.
Personalpronomen ersetzen Substantive und variieren je nach Geschlecht, Numerus und Kasus. Zum Beispiel: „ich“ (Nominativ), „mich“ (Akkusativ).
Präpositionen sind Wörter, die die Beziehung zwischen Substantiven und anderen Elementen im Satz beschreiben. Sie regieren bestimmte Kasus und können den Ort, die Zeit oder die Art und Weise angeben. Zum Beispiel: „auf dem Tisch“ (Lokalkasus), „nach der Schule“ (Temporal).
Verben werden im Deutschen konjugiert, was bedeutet, dass sie ihre Form je nach Person und Zeit ändern. Die Konjugation folgt bestimmten Mustern, die für verschiedene Verbklassen unterschiedlich sein können.
Die deutsche Satzstruktur folgt einem festen Wortstellungsmuster, bei dem das finite Verb in Position 2 im Hauptsatz steht. Die Reihenfolge der anderen Satzteile kann variieren, abhängig von Betonung und Kontext.

So mit diesem geballten Wissen können wir uns einen Trainer überlegen. Da wir möglichst viel abdecken wollen, überlegen wir uns erst einmal mögliche Fragen:
So auf die Schnelle fallen uns folgende Fragen ein:
Was ist der bestimmte Artikel für ‚Blume‘?
Was ist der unbestimmte Artikel für ‚Haus‘?
Gib das Präteritum von ‚kommen‘.
Bilde den Plural von ‚Kind‘.
Was ist der Akkusativ von ’sie‘?
Was ist der Dativ von ‚er‘?

Einfach wäre es nun die Fragen und die Antworten in eine Datei zu schrieben und wie bei den vorhergehenden Trainern. Dies ist aber nicht so schön. Schöner ist es die Fragen und Antworten in eine Datenbank zu schreiben.
Einfach ausgedrückt ist eine Datenbank einfach ein Ort, wo beliebige Daten gespeichert werden. Diese Sammlung von Daten ist strukturiert und effizient gespeichert, abgerufen, verwaltet und aktualisiert werden. Alle großen Apps wie Facebook, X, Amazon haben im Hintergrund Datenbanken laufen.

Wir verwenden für unseren Grammatik Trainer eine kleine simple Datenbank namens SQLite3. Die ist für uns vollkommen ausreichend. Das gute ist sie ist in Python schon vorhanden. Wir können also gleich loslegen.
Wie immer müssen wir die gewünschte Bibliothek importieren.

import sqlite3

Wir haben die erste Hürde überwunden und können die Datenbank anlegen.
Dies machen wir mit dem connect Befehl. Wenn es die Datenbank nicht gibt wird sie einfach angelegt, darum müssen wir uns nicht kümmern.

Verbindung zur SQLite-Datenbank herstellen oder erstellen

conn = sqlite3.connect("deutscheGrammatik.db")

So als nächstes müssen wir einen sogenannten Cursor definieren mit dem wir dann sogenannte SQL Befehle absetzen können.
Aber vorher ein kurzer Exkurs:
SQL (Structured Query Language) ist eine spezielle Programmiersprache, die für die Verwaltung und Abfrage von relationalen Datenbanken entwickelt wurde. Hier ist eine kurze Einführung in SQL:
Datenbanken und Tabellen: SQL wird verwendet, um Daten in Datenbanken zu organisieren. Eine Datenbank ist eine strukturierte Sammlung von Daten, die in Tabellen organisiert ist. Jede Tabelle besteht aus Zeilen und Spalten, wobei jede Zeile eine Datenaufzeichnung und jede Spalte ein bestimmtes Merkmal oder Attribut darstellt.
SQL-Befehle: SQL besteht aus verschiedenen Befehlen, mit denen Datenbanken erstellt, geändert und abgefragt werden können. Die wichtigsten SQL-Befehle sind:
CREATE DATABASE: Erstellt eine neue Datenbank.
CREATE TABLE: Erstellt eine neue Tabelle in einer Datenbank.
INSERT INTO: Fügt Daten in eine Tabelle ein.
SELECT: Ruft Daten aus einer Tabelle ab.
UPDATE: Aktualisiert Daten in einer Tabelle.
DELETE: Löscht Daten aus einer Tabelle.
ALTER TABLE: Ändert die Struktur einer Tabelle.
DROP TABLE: Löscht eine Tabelle.
Abfragen: Die SELECT-Anweisung wird verwendet, um Daten aus einer Tabelle abzurufen. Mit SQL können Sie auch Bedingungen und Filter verwenden, um spezifische Daten abzurufen. Zum Beispiel: SELECT Vorname, Nachname FROM Benutzer WHERE Alter > 30.
Klauseln: SQL verwendet verschiedene Klauseln, um Abfragen zu erweitern oder zu verfeinern. Beispiele für SQL-Klauseln sind WHERE, ORDER BY, GROUP BY, HAVING und JOIN, um nur einige zu nennen.
Datentypen: In SQL werden Datentypen verwendet, um den Typ der in einer Spalte gespeicherten Daten anzugeben. Beispiele für Datentypen sind INT (für ganze Zahlen), VARCHAR (für Zeichenketten) und DATE (für Datumsangaben).
Primärschlüssel: Eine Primärschlüsselspalte in einer Tabelle enthält eindeutige Werte für jede Zeile und dient zur Identifizierung von Datensätzen. Sie wird oft verwendet, um Tabellen zu verknüpfen.
Fremdschlüssel: Ein Fremdschlüssel ist eine Spalte in einer Tabelle, die auf die Primärschlüsselspalte einer anderen Tabelle verweist. Sie werden verwendet, um Beziehungen zwischen Tabellen herzustellen.
Transaktionen: SQL unterstützt Transaktionen, die eine Gruppe von Datenbankoperationen zu einer atomaren Einheit zusammenfassen. Transaktionen sind wichtig, um Datenkonsistenz und -sicherheit sicherzustellen.
Das mag jetzt kompliziert klingen und jetzt noch eine zweite Programmiersprache dazu, aber für unsere Zwecke reichen einige wenige Befehle – also keine Angst.

Wir brauchen um unsere Fragen und die Antworten zu speichern eine Tabelle. Wir nennen sie Fragen. In dieser Tabelle brauchen wir zwei Spalten, eine für die Frage und eine für die Antwort. Jede Tabelle sollte einen eindeutigen Schlüsselwert, den sogenannten Key, haben. So ein Key kann eine Zahl, aber auch ein Text sein – die meisten Datenbanken tun sich mit einer Zahl aber am einfachsten. Also definieren wir noch eine Key und nennen sie id.
Anhand unserer Überlegungen können, wir nun ein SQL basteln das uns die Tabelle erstellt.
Zu diesem Zweck gibt es den SQL Befehl CREATE TABLE – dann brauchen wir noch den Namen der Tabelle den wir anlegen wollen, also Fragen.
CREATE TABLE Fragen
Es folgt nun der Block in dem wir definieren, wie die Tabelle ausschauen soll, also unsere Splaten.
Zuerst der Key, der eine Zahl (Integer) ist und ein sogenannter PRIMARY KEY ist
Id INTEGER PRIMARY KEY,
Dann die frage die einen Text enthalten wird
frage TEXT
Und die dazugehörige Antwort
antwort Text
Eine Sache fehlt uns noch. Wir wollen, das die Tabelle nur einmal erstellt wird. Dafür gibt es den Befehl IF NOT EXISTS. Dies müssen wir nach CREATE TABLE einfügen.
Unser fertiges SQL lautet also:
CREATE TABLE IF NOT EXISTS Fragen ( id INTEGER PRIMARY KEY, frage TEXT, antwort TEXT)
Dieses SQL könnten wir nun direkt in einer Datenbank wie MSSQL oder Oracle mit leichter Modifikation des Syntax laufen lassen und es würde uns eine wunderschöne Tabelle erstellen.
Wir wollen das ganze in unserem Programm laufen lassen und deshalb verpacken wir das so:

Tabelle für die Fragen erstellen, wenn sie noch nicht existiert

cursor.execute('''CREATE TABLE IF NOT EXISTS Fragen (<br>id INTEGER PRIMARY KEY,<br>frage TEXT UNIQUE,<br>antwort TEXT<br>)''')

Als nächstes müssen wir eine Funktion programmieren, mit der wir Fragen und Antworten unserer Datenbank, genauer der Tabelle, hinzufügen können. Um eine neue Frage bzw. Antwort der Tabelle hinzufügen zu können, müssen wir den “insert” Befehl auf der Datenbank ausführen.
Der “insert” Befehl ist eigentlich recht einfach.
Die grundlegende Syntax des INSERT-Befehls sieht folgendermaßen aus:

INSERT INTO table_name (column1, column2, column3, …) VALUES (value1, value2, value3, …);

Der INSERT-Befehl in SQL wird verwendet, um neue Datensätze in eine Tabelle einer Datenbank einzufügen. Er ermöglicht das Hinzufügen von Daten in eine vorhandene Tabelle, wobei die Daten in die entsprechenden Spalten eingefügt werden. Hier ist eine Beschreibung des INSERT-Befehls in SQL:
Die grundlegende Syntax des INSERT-Befehls sieht folgendermaßen aus:
INSERT INTO table_name (column1, column2, column3, …)
VALUES (value1, value2, value3, …);

table_name: Hier wird der Name der Tabelle angegeben, in die die Daten eingefügt werden sollen.
column1, column2, column3, …: Dies sind die Namen der Spalten in der Tabelle, in die Daten eingefügt werden sollen. Sie sollten in Klammern nach table_name aufgelistet werden.
VALUES: Dieses Schlüsselwort signalisiert den Beginn der Werte, die in die Tabelle eingefügt werden.
value1, value2, value3, …: Hier werden die tatsächlichen Werte angegeben, die in die entsprechenden Spalten eingefügt werden. Die Reihenfolge der Werte sollte der Reihenfolge der Spalten entsprechen.

Angenommen wir wollen nun die Frage “Was ist der unbestimmte Artikel für ‚Haus‘? “ mit der Antwort “ein” unserer Datenbank hinzufügen, dann würde unser INSERT Befehl so aussehen:

INSERT INTO FRAGEN (frage, antwort) VALUES (‘Was ist der unbestimmte Artikel für “Haus”? ‘,’ein’)

Was fällt uns gleich auf. Es fehlt ein Feld – die id! Das ist jedoch kein Fehler, sondern eine Eigenart. Die Spalte id da wir sie als Primary Key deklariert haben, wird der Wert von SQLite automatisch beim insert vergeben und wir sind fein raus weil wir uns darum nicht kümmern müssen.
In unserem Code definieren wir nun eine Funktion namens frage_hinzufuegen und führen unser insert aus.

Eine Frage / Antwort der Tabelle hinzufügen

def frage_hinzufuegen (frage, antwort):
     cursor.execute("INSERT INTO Fragen (frage, antwort, ) VALUES (?, ?)",(frage, antwort))
     conn.commit()
     Print(f"Die Frage '{frage}' wurde zur Datenbank hinzugefügt.")

Wichtig ist das wir conn.commit() aufrufen, damit wir das ganze auch auf der Datenbank ausgeführt wird.

So als nächstes müssen wir nun eine Funktion bauen, die einen zufälligen Datensatz aus unserer Tabelle lesen.
Dazu gibt es in SQL den SELECT-Befehl. SELECT wird verwendet, um Daten aus einer Datenbank abzurufen. Er ermöglicht es, bestimmte Spalten aus einer oder mehreren Tabellen auszuwählen und optional Filterkriterien anzuwenden. Hier ist die grundlegende Syntax des SELECT-Befehls:

SELECT column1, column2, … FROM table_name WHERE condition;
  1. SELECT column1, column2, ...: Hier können wir die Namen der Spalten angeben, die wir aus der Tabelle abrufen möchten. Mit SELECT * können wir alle Spalten abrufen.
  2. FROM table_name: In diesem Teil des SQL Befehls definieren wir aus welcher Tabelle wir die Daten abrufen möchsten.
  3. WHERE condition (optional): Dieser Teil des Befehls ermöglicht es uns, Bedingungen festzulegen, die die ausgewählten Datensätze einschränken. Nur die Datensätze, die die angegebene Bedingung erfüllen, werden zurückgegeben. Wenn wir keine Bedingung angeben, werden alle Datensätze zurück geliefert.
  4. ‘ORDER by column1, column2,….’ (optional): Mit dieser Anweisung können wir das Ergebnis sortieren. Wenn wir mehr als eine Spalte angeben dann wird zuerst nach der ersten Spalte, dann nach der zweiten, usw. Sortiert.

Zum Beispiel, wenn Sie alle Spalten aus der Tabelle aus unserer Tabelle Fragen ausgeben werden, würde der SQL-Befehl so aussehen:

SELECT * FROM Fragen;

Nach diesem kurzen Exkurs kehren wir wieder zurück zu unserem Programm. Wir wollen ja nicht alle sondern nur eine zufällige Zeile auslesen.
Dazu bietet SQLite einen Befehl ORDER BY RANDOM().
Wenn wir unser Select von oben um ein ORDER BY RANDOM() erweitern, so bekommen wir unsere Fragen in zufälliger Reihenfolge.
Das ist schon nah dran an dem was wir wollen, jedoch brauchen wir ja nur eine. Dazu können wir den SQL Befehl LIMIT 1 hinzufügen.

So nun müssen wir das ganze nur noch in eine Python Funktion packen.

def frage_auslesen():
    conn = sqlite3.connect("Gramatikfragen.db")
    # Ein Cursor-Objekt erstellen, um Datenbankabfragen auszuführen
    cursor = conn.cursor()

    cursor.execute("Select * from FRAGEN ORDER BY RANDOM() LIMIT 1")
    frage = cursor.fetchone()
    conn.close()
    return frage

Eine Funktion fehlt uns noch, die was uns die Frage stellt. Die klopfen wir nach Schema F rein:

def frage_stellen():
     # Zufällige Auswahl einer Frage
     rowid,frage, antwort = frage_auslesen()
     print(frage)
     benutzerantwort = input("Antwort: ").strip().lower()
     if benutzerantwort == antwort:
        print("Richtig!\n")
        return True
     else:
        print(f"Falsch. Die richtige Antwort ist '{antwort}'.\n")
        return False

Wenn wir alle Teile zusammen fügen, erhalten wir gpiwonka/Deutsche-Grammatik (github.com).