lehrkraefte:blc:informatik:glf22:python:woertlitrainer

This is an old revision of the document!


Wörtlitrainer

  • Input: Liste von Wortpaaren (ebenfalls eine Liste). Z.B.
voci = [ ["ein Byte", "un octet"], ["ein Computer", "un ordinateur"], ["eine Software", "un logiciel"] ]
  • Output: Vorläufig keiner
  • Für jedes paar in der Wortliste:
    • ok = False
    • while not ok:
      • Wort auf Deutsch anzeigen
      • Übersetzung vom Benutzer einlesen, in Variable eingabe speichern.
      • Wenn eingabe gleich dem französischen Wort, die Variable ok auf True setzen

Testen, verändern und verstehen Sie folgende Mini-Programme:

Vokabular in verschachtelter Liste festlegen und auf einzelne Elemente zugreifen:

voci = [ ["ein Byte", "un octet"], ["ein Computer", "un ordinateur"], ["eine Software", "un logiciel"] ]
print(voci)
print(voci[0])
print(voci[0][1])

Vokabular durchgehen:

voci = [ ["ein Byte", "un octet"], ["ein Computer", "un ordinateur"], ["eine Software", "un logiciel"] ]
for paar in voci:
  print(paar)
  print(f"Deutsch: {paar[0]}, französisch {paar[1]}")

Wort eingeben:

import sys   # sys einbinden
print("Was ist dein Name?")
name = sys.stdin.readline().strip()     # Einlesen, Enter-Taste am Schluss nicht in Name speichern.
print(f"Hallo {name}, alles klar?")

Wörter vergleichen:

name = "Rumpelstilzchen"
if name == "Rumpelstillzchen":
   print("Du hast meinen Namen erraten!")
else:
   print("Ach wie gut dass niemand weiss, dass ich Rumpelstillzchen heiss'")

Programmieren Sie den Wörtli-Trainer in Python.

Hinweis: wenn paar ein Wortpaar ist, dann ist paar[0] das deutsche und paar[1] das französische Wort.

Lösungsvorschlag

Lösungsvorschlag

import sys 
voci = [ ["ein Byte", "un octet"], ["ein Computer", "un ordinateur"], ["eine Software", "un logiciel"] ]
 
# Wiederholen, für jedes paar in der Liste
for paar in voci:
    ok = False # Benutzer hat noch nicht richtig geantwortet
    anzahlfehler = 0  # Für dieses Wort, noch keinen Fehler
    while not ok:
        # Wort auf Deutsch anzeigen
        print(f"Übersetze: {paar[0]}")
        if anzahlfehler>2:  # Mehr als zwei Fehler? Dann Wort anzeigen
            print(f"   Hinweis: {paar[1]}")
        # Eingabe vom Benutzer einlesen (Zeilenumbruch abschneiden mit strip)
        eingabe = sys.stdin.readline().strip()
 
        if eingabe==paar[1]:  # gleich dem Fremdwort?
            ok  = True        # Damit wird die Schleife dann beendet
        else:
            anzahlfehler = anzahlfehler + 1    # Fehlerzähler erhöhen

Bessere Datenstruktur

Dass das deutsche Wort über den Index [0] und das andere über [1] aufgerufen werden muss, ist weder einsichtig noch praktisch, besonders, wenn das Programm noch ausgebaut werden soll.

Es gibt in Python neben Arrays (von 0 bis $n-1$ durchnummerierte Einträge) auch Dictionaries, wo die Einträge durch Zeichenketten anstatt Zahlen referenziert werden:

voci = [{"d":"ein Byte", "f":"un octet"},\
        {"d":"ein Computer", "f":"un ordinateur"},\
        {"d":"eine Software", "f":"un logiciel"}]
print("voci = ", end="")
print(voci)
print("voci[1] = ", end="")
print(voci[1])
print("voci[1]['d'] = ", end="")
print(voci[1]["d"])

Passen Sie Ihren Wörtli-Trainer an die neue Datenstruktur an.

JSON (JavaScript Object Notation) ist eine im Web-Bereich häufig verwendete Art, strukturierte Daten zu speichern. Diese kann auch sehr einfach in Python gelesen und geschrieben werden. Für Arrays und Dictionaries ist der Syntax sogar gleich. Diese Dateien können damit auch sehr einfach von Menschen gelesen und bearbeitet werden. Diverse Programmiersprachen (auch Python) haben bereits Bibliotheken, um JSON-Dateien ganz einfach zu lesen und zu schreiben.

JSON-Datei schreiben:

import json
voci = [{"d":"ein Byte", "f":"un octet"},\
        {"d":"ein Computer", "f":"un ordinateur"},\
        {"d":"eine Software", "f":"un logiciel"}]
with open("voci.json", "w") as f:   # Datei zum Schreiben öffnen
  f.write(json.dumps(voci, indent=2))    # Daten in Datei schreiben
  f.write("\n")

Führen Sie obiges Programm aus und betrachten Sie dann die generierte Datei voci.json in VSCode.

JSON-Datei lesen (dieses Programm funktioniert erst, wenn die Datei voci.json korrekt erzeugt wurde):

import json
with open("voci.json", "r") as f:   # Datei zum Lesen öffnen
  voci = json.loads(f.read())       # Inhalt lesen, interpretieren und in die Variable voci speichern.
 
# Jetzt kann mit der Variablen voci wie vorhin gearbeitet werden:
print(voci)

Passen Ihren Wörtli-Trainer so an, dass die Wörter von der Datei voci.json gelesen werden.

Ziel ist es, die Wörter-Datei frei wählen zu können. Dies soll direkt auf der Kommandozeile geschehen:

import os
import sys
 
datei = "voci.json"
if len(sys.argv)>1:  # Falls es ein zusätzliches Kommandozeilenargument gibt
    datei = sys.argv[1]
print(datei)
if not os.path.exists(datei):   # Falls die Datei nicht existiert, abbrechen
    print(f"Sorry, die Datei {datei} existiert nicht!")
    exit(-1)  # Abbruch mit Fehler

Testen Sie obiges Programm auch auf der Kommandozeile und geben Sie existierende und nicht existierende Dateien als Argument an (Sie können auch mit VSCode den Startknopf drücken, und dann in der internen Kommandozeile mit Pfeil nach oben das Kommando nochmals holen und eine Datei als Argument hinten hinzufügen).

Bauen Sie die Möglichkeit, eine Datei auf der Kommandozeile zu wählen, in Ihren Wörtli-Trainer ein.

Lösungsvorschlag

Lösungsvorschlag

voci-mit-dict-von-datei.py
import sys 
import os
import json
 
##################
# Daten einlesen #
##################
 
# Datei bestimmen (default oder von der Kommandozeile)
datei = "voci.json"
if len(sys.argv)>1:  # Falls es ein zusätzliches Kommandozeilenargument gibt
    datei = sys.argv[1]
print(f"Versuche Daten aus der Datei {datei} einzulesen")
if not os.path.exists(datei):   # Falls die Datei nicht existiert, abbrechen
    print(f"Sorry, die Datei {datei} existiert nicht!")
    exit(-1)  # Abbruch mit Fehler
 
# Daten einlesen
with open(datei, "r") as f:   # Datei zum Lesen öffnen
  voci = json.loads(f.read())       # Inhalt lesen, interpretieren und in die Variable voci speichern.
 
##########
# Lernen #
##########
 
# Wiederholen, für jedes paar in der Liste
for paar in voci:
    ok = False # Benutzer hat noch nicht richtig geantwortet
    anzahlfehler = 0
    while not ok:
        # Wort auf Deutsch anzeigen
        print(f"Übersetze: {paar['d']}")
        if anzahlfehler>2:
            print(f"   Hinweis: {paar['f']}")
        # Eingabe vom Benutzer
        eingabe = sys.stdin.readline().strip()
 
        if eingabe==paar['f']:  # gleich dem Fremdwort?
            ok  = True
        else:
            anzahlfehler = anzahlfehler + 1

Intelligenteres Abfragen

Wörter, die man schon gut kann, sollten nicht (oder kaum) abgefragt werden. Andere sollten hingegen häufiger abgefragt werden. Auch sollte die Reihenfolge der Wörter zufällig sein.

Dazu gibt es verschiedenste Ansätze. Ein ganz einfacher ist folgender:

  • Zu jedem Wortpaar wird zusätzlich ein Score s gespeichert.
  • Ganz am Anfang sind alle Scores 0
  • Hat man das Wort richtig, wird s um 1 erhöht.
  • Hat man das Wort falsch, wird s um 1 erniedrigt, aber nicht kleiner als 0
  • Das Programm soll erst Wörter mit kleinstem Score abfragen.

Zusätzliches Feld hinzufügen:

voci = [{"d":"ein Byte", "f":"un octet"},\
        {"d":"ein Computer", "f":"un ordinateur"},\
        {"d":"eine Software", "f":"un logiciel", "s":42}]
for paar in voci:
  if not "s" in paar:  # Gibt es den Schlüssel s bereits? Falls nein, hinzufügen.
    paar["s"] = 0
print(voci)

Ein zufälliges Wort aus allen Wörtern mit kleinstem Score kann wie folgt ausgewählt werden (was noch viel effizienter gemacht werden könnte):

  • Wir sortieren alle Wörter, kleinste Scores zuerst (plus eine Zufallszahl zwischen 0 und 1, damit die Reihenfolge bei gleichem Score zufällig wird).
  • Wir wählen das erste Wort aus

Sämtliche Wörter zu sortieren ist «overkill», man könnte auch einfach das kleinste Score suchen und dabei eines zufällig merken. Oder es geht noch effizienter, z.B. mit einem Binary Heap.

import random
voci = [{'d': 'ein Byte', 'f': 'un octet', 's': 1}, {'d': 'ein Computer', 'f': 'un ordinateur', 's': 0}, {'d': 'eine Software', 'f': 'un logiciel', 's': 0}]
 
for i in range(10):  # 10 mal wiederholen, zum Testen
    # Vokabular sortieren, wobei ein Eintrag mit score+zufallszahl bewertet wird:
    voci = sorted(voci, key = lambda eintrag: eintrag['s']+random.random())
    print(f"Erster Eintrag ist jetzt: {voci[0]}")

Endfassung

Erweitern Sie Ihr Programm schrittweise wie folgt und testen Sie es fortlaufend:

  • Anstatt die Wörter der Reihe nach durchzugehen, solle diese zufällig und nach Score abgefragt werden und das beliebig oft. Benutzen Sie dazu eine Endlosschleife while true:. Um das Programm zu beenden benutzen Sie Ctrl-C.
  • Das Score soll angepasst werden, je nach Antwort.
  • Das Programm soll beendet werden, wenn man ein z.B. nur ein 'x' eingibt (oder ein anderes Zeichen oder Wort).
  • Wenn das Programm beendet wird, sollen die Daten wieder geschrieben werden, damit die Scores erhalten bleiben.
  • Erstellen Sie eine JSON-Datei mit dem aktuellen Französisch-Vokubular (gerne auch kollaborativ, so dass jeder z.B. 10 Wörter einträgt und dann die Dateien vereinigt werden).

Lösungsvorschlag

Lösungsvorschlag

voci-v1.py
import sys 
import os
import json
import random
 
##################
# Daten einlesen #
##################
 
# Datei bestimmen (default oder von der Kommandozeile)
datei = "voci.json"
if len(sys.argv)>1:  # Falls es ein zusätzliches Kommandozeilenargument gibt
    datei = sys.argv[1]
print(f"Versuche Daten aus der Datei {datei} einzulesen")
if not os.path.exists(datei):   # Falls die Datei nicht existiert, abbrechen
    print(f"Sorry, die Datei {datei} existiert nicht!")
    exit(-1)  # Abbruch mit Fehler
 
# Daten einlesen
with open(datei, "r") as f:   # Datei zum Lesen (read) öffnen
  voci = json.loads(f.read())       # Inhalt lesen, interpretieren und in die Variable voci speichern.
 
# Scores hinzufügen, falls nötig
for paar in voci:
  if not "s" in paar:  # Gibt es den Schlüssel s bereits? Falls nein, hinzufügen.
    paar["s"] = 0
 
##########
# Lernen #
##########
 
# Wiederholen, bis der Benutzer abbricht
programmEnde = False
print("Hinweis: Durch Eingabe von 'x' kann das Programm beendet werden.")
while not programmEnde:  # Endlos-Schleife
    # Wörter Sortieren:
    voci = sorted(voci, key = lambda eintrag: eintrag['s']+random.random())
    # Aktuelles paar ist das erste in der Liste
    paar = voci[0]
    # Abfrage vorbereiten
    ok = False # Benutzer hat noch nicht richtig geantwortet
    anzahlfehler = 0
    while not ok:  # Wiederholen, bis der Benutzer richtig geantwortet hat.
        # Wort auf Deutsch anzeigen
        print(f"Übersetze: {paar['d']}     (score {paar['s']})")
        if anzahlfehler>2:
            print(f"   Hinweis: {paar['f']}")
        # Eingabe vom Benutzer
        eingabe = sys.stdin.readline().strip()
        if eingabe=="x":
            programmEnde = True
            break     # innere while-Schlaufe verlassen
 
        if eingabe==paar['f']:  # gleich dem Fremdwort?
            ok  = True
            if anzahlfehler==0:
                paar['s']+=2
            elif anzahlfehler==1:
                paar['s']+=1
            else:
                paar['s'] -= 1
                if (paar['s']<0):
                    paar['s']=0
        else:
            anzahlfehler = anzahlfehler + 1
 
# Daten speichern:
with open(datei, "w") as f:   # Datei zum Schreiben (write) öffnen
  f.write(json.dumps(voci, indent=2))    # Daten in Datei schreiben
  f.write("\n")

Besserer Programmierstil

Im Moment ist das Programm ein schon ziemlich unübersichtlicher Klumpen. Um das Programm übersichtlicher zu gestalten, werden wir einzelne Programmteile in Funktionen auslagern, damit das Programm am Schluss in 3 Zeilen passt:

voci, datei = daten_einlesen()
abfragen(voci)
daten_speichern(voci, datei)

Lösungsvorschlag

Lösungsvorschlag

voci-v2.py
import sys 
import os
import json
import random
 
 
 
def wort_bestimmen(voci):
    voci.sort(key = lambda eintrag: eintrag['s']+random.random())
    # Aktuelles paar ist das erste in der Liste
    return voci[0]
 
 
def wort_abfragen(paar, anzahlfehler):
        print(f"Übersetze: {paar['d']}     (score {paar['s']})")
        if anzahlfehler>2:
            print(f"   Hinweis: {paar['f']}")
        # Eingabe vom Benutzer
        eingabe = sys.stdin.readline().strip()
        return eingabe
 
 
def wort_korrekt(eingabe, paar, anzahlfehler):
    if (eingabe!=paar['f']):
        return False # Funktion sofort beenden.
    # Also ist die eingabe korrekt, kein if nötig, weil return die Funktion beendet
    if anzahlfehler>1:
        paar['s'] -= 1
        if (paar['s']<0):
            paar['s']=0
        return True  # Funktion beenden
    # Weniger als zwei Fehler, also Score nach oben anpassen
    paar['s'] += 2-anzahlfehler
    return True
 
 
def wort_lernen(paar):
    """Liefert True, wenn das Programm abgebrochen werden soll. Sonst False."""
    anzahlfehler = 0
    while True:  # Endlosschleife
        eingabe = wort_abfragen(paar, anzahlfehler)
        if eingabe=="x":
            return True   # Fertig, mit Programmabbruch
        if wort_korrekt(eingabe, paar, anzahlfehler):
            return False  # Fertig ohne Programmabbruch
        anzahlfehler = anzahlfehler + 1
 
 
def abfragen(voci):
    """Abfragen, bis der Benutzer das Programm beendet"""
    while True:
        paar = wort_bestimmen(voci)
        programmAbbruch = wort_lernen(paar)
        if programmAbbruch:
            break
 
 
def dateinamen_bestimmen():
    datei = "voci.json"
    if len(sys.argv)>1:  # Falls es ein zusätzliches Kommandozeilenargument gibt
        datei = sys.argv[1]
    print(f"Versuche Daten aus der Datei {datei} einzulesen")
    if not os.path.exists(datei):   # Falls die Datei nicht existiert, abbrechen
        print(f"Sorry, die Datei {datei} existiert nicht!")
        exit(-1)  # Abbruch mit Fehler
    return datei
 
 
def scores_hinzufuegen(voci):
    for paar in voci:
        if not "s" in paar:  # Gibt es den Schlüssel s bereits? Falls nein, hinzufügen.
            paar["s"] = 0
 
 
def daten_einlesen():
    """Daten einlesen, entweder aus Standarddatei voci.json oder von der Kommandozeile."""
    datei = dateinamen_bestimmen()
    # Daten einlesen
    with open(datei, "r") as f:   # Datei zum Lesen (read) öffnen
        voci = json.loads(f.read())       # Inhalt lesen, interpretieren und in die Variable voci speichern.
    scores_hinzufuegen(voci)
    return voci, datei
 
 
def daten_speichern(voci, datei):
    """Speichert die Daten im JSON-Format in den gegebenen Dateinamen"""
    with open(datei, "w") as f:   # Datei zum Schreiben (write) öffnen
        f.write(json.dumps(voci, indent=2))    # Daten in Datei schreiben
        f.write("\n")
    print("Daten gespeichert")
 
#####################
### Hauptprogramm ###
#####################
 
voci, datei = daten_einlesen()
abfragen(voci)
daten_speichern(voci, datei)
  • lehrkraefte/blc/informatik/glf22/python/woertlitrainer.1670834002.txt.gz
  • Last modified: 2022/12/12 09:33
  • by Ivo Blöchliger