Automaten programmieren

Wir wollen nun versuchen mit dem gelernten Wissen um Automaten, einige Anwendungen mit Python zu programmieren. Wir programmieren also mit Zuständen und Zustandsübergängen.

Zustand
ein Zustand können wir uns mit einer Variablen merken
Zustandsübergang
eine Aktion (z.B. ein Unterprogramm) welches etwas macht und den Zustand wechselt

Enums

Wir könnten uns Zustände einfach mit einer int-Variable merken. Dann müssen wir aber wissen welche Zahl welchem Zustand entspricht. Für solche Zwecke gibt es die Klasse Enum. Damit können wir eine Aufzählung von Werten erstellen und diesen sinnvolle Namen geben:

from enum import Enum

class Zustand(Enum):
	LICHT_AUS = 1
	LICHT_AN = 2
1
2
3
4
5

Wir erstellen eine eigene Klasse Zustand die von Enum erbt. Unsere Klasse definiert zwei Konstante Werte: LICHT_AUS und LICHT_AN. Die Werte sind uns egal, müssen aber unterschiedlich sein!

Wir können nun über die Klasse Zustand eine Variable auf diese vordefinierten Werte setzen und auch vergleichen:

zustand = Zustand.LICHT_AUS

def schalter_klick():
	if zustand is Zustand.LICHT_AUS:
		zustand = Zustand.LICHT_AN
		print("Licht eingeschaltet")
1
2
3
4
5
6

Aufgabe: Enums

Was müsste man im obigen Beispiel erweitern, damit das Licht wieder ausgeschaltet werden kann?

Getränkeautomat

Wir schauen das bereits bekannte Beispiel des Getraenkeautomates an. Zur Wiederholung nochmals das Zustandsübergangsdiagramm und die Zustandsübergangstabelle:

δ1.-2.-spritecoke
0.-1.-2.-
1.-2.-3.-
2.-3.-
3.-0.-0.-

Wir wollen diesen Automaten objektorientiert mit Python programmieren. Dazu brauchen wir also:

  • ein Enum für die 4 Zustände
  • die Klasse «Getränkeautomat»
  • einen Startzustand
  • 4 Methoden für die 4 Übergänge die beim Aufruf auch etwas ausgeben
  • einige Tests, um zu schauen ob es funktioniert

Das Ergebnis könnte wie folgt aussehen:

from enum import Enum

class Zustand(Enum):
    START = 1
    BETRAG_1 = 2
    BETRAG_2 = 3
    BETRAG_3 = 4
    
class Getraenkeautomat:
    def __init__(self):
        self.zustand = Zustand.START
        
    def eingabe_1(self):
        if self.zustand is Zustand.START:
            print("1.- eingegeben")
            self.zustand = Zustand.BETRAG_1
        elif self.zustand is Zustand.BETRAG_1:
            print("1.- eingegeben")
            self.zustand = Zustand.BETRAG_2
        elif self.zustand is Zustand.BETRAG_2:
            print("1.- eingegeben")
            self.zustand = Zustand.BETRAG_3
        else:
            print("1.- kommt wieder raus")
        
    def eingabe_2(self):
        if self.zustand is Zustand.START:
            print("2.- eingegeben")
            self.zustand = Zustand.BETRAG_2
        elif self.zustand is Zustand.BETRAG_1:
            print("2.- eingegeben")
            self.zustand = Zustand.BETRAG_3
        else:
            print("2.- kommt wieder raus")
        
    def knopf_sprite(self):
        if self.zustand is Zustand.BETRAG_3:
            print("sprite kommt raus")
            print("---")      
            self.zustand = Zustand.START
        else:
            print("nichts passiert")
        
    def knopf_coke(self):
        if self.zustand is Zustand.BETRAG_3:
            print("coke kommt raus")
            print("---")            
            self.zustand = Zustand.START
        else:
            print("nichts passiert")
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50

Hinweis

Wir speichern den Betrag nicht als Zahl in einer Variablen, sondern der verfügbare Betrag entspricht einem Zustand.

Wir können nun einige Tests mit unserem Getränkeautomaten-Objekt anstellen und schauen, ob ein Sprite oder eine Cola rauskommt:

from getraenkeautomat import Getraenkeautomat

test = Getraenkeautomat()
test.eingabe_1()
test.eingabe_1()
test.eingabe_1()
test.knopf_coke()

test.eingabe_1()
test.eingabe_2()
test.eingabe_1()
test.knopf_sprite()

test.eingabe_2()
test.eingabe_2()
test.knopf_coke()
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

PythonKara

PythonKara

Kara gibt es auch in einer Python-Version:
https://www.swisseduc.ch/informatik/karatojava/pythonkara/index.html

Die Aufgaben sind ähnlich, man löst sie aber mit Python. Dabei kann man die Sensoren direkt abfragen. Je nach Aufgabe braucht es trotzdem Zustände!

Aufgabe: PythonKara

  • Installiere und starte PythonKara
  • Löse einige der eingebauten Aufgaben
  • überlege dir, wie und wo du Zustände einsetzt

E-Mail-Checker

Bei einer Benutzereingabe soll der eingegebene Text auf ein gültiges E-Mail-Format überprüft werden.

Die vereinfachten Regeln lauten wie folgt:

  • Gültige Zeichen: a-z und 0-9
  • Reihenfolge
    • Start mit mindestens einem Zeichen
    • dann ein @
    • wieder mindestens ein Zeichen
    • dann ein Punkt
    • nochmals mindestens ein Zeichen

also wäre z.B. ef2023@mygymer.ch eine gültige E-Mail-Adresse

Aufgabe: Zustandsübergangsdiagramm

Erstelle ein Zustandsübergangsdiagramm für die Reihenfolge der Zeichen einer E-Mail-Adresse:

  • Zustände brauchst du um die Regeln überprüfuen zu können
  • Übergänge entsprechen den einzelnen Zeichen der E-Mail-Adresse

Aufgabe: Python

Implementiere deinen Automaten in Python:

  • schreibe ein Unterprogramm check_email(adresse) welches das Argument adresse auf gültiges E-Mail-Format überprüft
  • liefert True (gültig) oder False (ungültig)
  • Teste dein Unterprogramm indem du vom Benutzer eingegebene E-Mail-Adressen überprüfst

Tipp: Zeichen-um-Zeichen

Ein String kann in einer for-Schleife Zeichen um Zeichen durchlaufen werden:

text = "Hallo"
for zeichen in text:
	print(zeichen)
1
2
3

Tipp: Benuztereingaben

Ein vom Benutzer eingegebener String kann mit input() eingelesen werden. Die Eingabe kann auch in einer Schleife wiederholt werden, optional auch mit Abbruch bei bestimmter Eingabe:

eingabe = ""
while eingabe != "x":
	eingabe = input("bitte E-Mail-Adresse eingeben (x: Abbruch): ")
	print(eingabe)
1
2
3
4