![]() |
||
| Material |
OOP
Grundlagen
Delphi
Software-Technik
Bonsai
Digitaltechnik
Ereignisse
Grafik
UML
Netze
Fischertechnik
Tipps
Werkzeuge
Literatur
Automaten
Sprachen
Datenbanken
XML
Prolog
Berechenbarkeit
|
|
|
Automatentheorie |
Der zu realisierende Automat sei durch folgenden Graphen (aus Informatik heute, Bd.2, S.84ff) gegeben:

/1/ Das Programm soll objektorientiert nach dem MVC-Muster entwickelt werden.
/2/ Der Automat soll übersichtlich beschrieben werden.
/3/ Das Programm soll einfach an andere Automaten angepasst werden können.
/4/ Eingaben sollen nur aus dem Eingabe-Alphabet möglich sein.
/5/ Zu Kontrollzwecken soll der aktuelle Zustand änderbar sein.
/6/ Ein- und Ausgaben sollen protokolliert werden.


Zunächst soll die TAutomat-Klasse besprochen werden. Wie man sieht, werden selbstdefinierte Aufzähltypen TZustand, TEingabe, TAusgabe verwendet. Diese Typen legen die Zustände, das Eingabe- und das Ausgabe-Alphabet fest. Die Übergangs- und die Ausgabefunktion sind ebenfalls außerhalb der Klasse in Array-Konstanten festgelegt.
const
{ 0. Automatenname eintragen }
automatname = 'Getraenkeautomat, informatik heute, Bd.2, S.84ff';
type
{ 1. Zustandsmenge, Eingabe- und Ausgabealphabet anpassen }
TZustand = (z0,z50,z100,z150);
TEingabe = (e50,e100,eK,eW);
TAusgabe = (a50,a100,a150,aG,aN);
const
{ 2. Startzustand eintragen }
startzustand = z0;
{ 3. Klartextentsprechungen eintragen }
zText : array[TZustand] of string =
('kein Geld','0,50 DM','1,00 DM','1,50 DM');
eText : array[TEingabe] of string =
('50 Pf','1 DM','Korrekturtaste','Warenhebel');
aText : array[TAusgabe] of string =
('50 Pf','1 DM','1 DM und 50 Pf','Getränk','nichts');
{ 4a. Automatentafel eingeben, auf Zeilen und Spalten achten }
fue : array[TZustand,TEingabe] of TZustand =
((z50,z100,z0,z0), { fue(z0,e50), fue(z0,e100), ... }
(z100,z150,z0,z50), { fue(z50,e50), fue(z50,e100), ... }
(z150,z100,z0,z100),
(z150,z100,z0,z0));
{ 4b. Automatentafel eingeben, auf Zeilen und Spalten achten }
fa : array[TZustand,TEingabe] of TAusgabe =
((aN,aN,aN,aN), { fa(z0,e50), fa(z0,e100), ... }
(aN,aN,a50,aN), { fa(z1,e50), fa(z1,e100), ... }
(aN,a100,a100,aN),
(a50,a100,a150,aG));
Damit ist die TAutomat-Klasse universell für alle Transduktoren. Zur Realisierung eines anderen Automaten sind lediglich die rot gekennzeichneten Stellen zu ändern, an der eigentlichen Automaten-Klasse ändert sich nichts.
TAutomat hält den aktuellen Zustand, die gemachte Eingabe und die letzte Ausgabe fest. Außer einfachen Set- und Get-Methoden und einem erweiterten Konstruktor gibt es nur die Methode verarbeiteEingabe, die auch verblüffend einfach ist:
constructor TAutomat.create; begin inherited create; zustand := startzustand; end; ... procedure TAutomat.verarbeiteEingabe; begin ausgabe := fa[zustand,eingabe]; zustand := fue[zustand,eingabe]; end;
Die grafische Benutzeroberfläche ist völlig unabhängig vom speziellen Automaten, dh. an der Unit uGUI muss nichts geändert werden. Perfekte Wiederverwertung!
Interessant ist die Verwendung von ComboBoxen für Zustand, Eingabe und Ausgabe.
Die auswählbaren Items werden beim Programmstart aus den entsprechenden Arrays in die
ComboBoxen eingelesen. Damit ist sichergestellt, dass für Eingaben und Zustände nur erlaubte
Werte ausgewählt werden können. Die Auswahlliste bei der Ausgabe dient der Information über
das Ausgabe-Alphabet, das hier eingesehen werden kann.
Details für Belegung und Auslesen der Auswahlisten/ComboBoxen finden sich in folgenden
Quelltext-Auszügen.
...
// Eingabealphabet in ComboBox eintragen
for e := Low(TEingabe) to High(TEingabe) do
cbEingabe.Items.Add(eText[e]);
cbEingabe.ItemIndex := 0;
...
procedure TForm1.bVerarbeiteClick(Sender: TObject);
begin
if (cbZustand.ItemIndex <> -1) and (cbEingabe.ItemIndex <> -1)
then
begin
automat.setZustand(TZustand(cbZustand.ItemIndex));
automat.setEingabe(TEingabe(cbEingabe.ItemIndex));
automat.verarbeiteEingabe;
mEingaben.Lines.Add(eText[automat.getEingabe]);
cbZustand.ItemIndex := Ord(automat.getZustand);
cbAusgabe.ItemIndex := Ord(automat.getAusgabe);
mAusgaben.Lines.Add(aText[automat.getAusgabe]);
end;
end;
Download: getraenkeautomat.zip
Durch die Möglichkeit, den Zustand zu setzen, kann man den Automaten bequem völlig durchtesten. Man geht Zustand für Zustand durch, setzt die entsprechenden Eingaben und kontrolliert den Folgezustand sowie die Ausgabe.
Fehleingaben, die man in den ComboBoxen versucht, werden durch Kontrollen auf den ItemIndex -1 (nicht ausgewählt) abgefangen.
Realisiere folgenden Übungsautomaten mit obiger Vorlage.


Im folgenden Quelltext sind die für die Änderung auf einen anderen Automaten relevanten Stellen rot markiert:
unit uAuto; { mk, 29.1.03 , endlicher Automat, ohne MVC, ohne OOP }
{ Getränkeautomat aus Informatik heute, Bd.2, S.84ff }
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
StdCtrls;
type
{ 1. Zustandsmenge, Eingabe- und Ausgabealphabet anpassen }
TZustand = (z0,z50,z100,z150);
TEingabe = (e50,e100,eK,eW);
TAusgabe = (a50,a100,a150,aG,aN);
const
{ 2. Klartextentsprechungen eintragen }
zText : array[TZustand] of string =
('kein Geld','0,50 DM','1,00 DM','1,50 DM');
eText : array[TEingabe] of string =
('50 Pf','1 DM','Korrekturtaste','Warenhebel');
aText : array[TAusgabe] of string =
('50 Pf','1 DM','1 DM und 50 Pf','Getränk','nichts');
{ 3a. Automatentafel eingeben, auf Zeilen und Spalten achten }
fue : array[TZustand,TEingabe] of TZustand =
((z50,z100,z0,z0), { fue(z0,e50), fue(z0,e100), ... }
(z100,z150,z0,z50), { fue(z50,e50), fue(z50,e100), ... }
(z150,z100,z0,z100),
(z150,z100,z0,z0));
{ 3b. Automatentafel eingeben, auf Zeilen und Spalten achten }
fa : array[TZustand,TEingabe] of TAusgabe =
((aN,aN,aN,aN), { fa(z0,e50), fa(z0,e100), ... }
(aN,aN,a50,aN), { fa(z1,e50), fa(z1,e100), ... }
(aN,a100,a100,aN),
(a50,a100,a150,aG));
type
TForm1 = class(TForm)
{ 4. für jede Eingabe einen Button }
be100: TButton; be50: TButton; beK: TButton; beW: TButton;
eZustand: TEdit; lZustand: TLabel; eAusgabe: TEdit; lAusgabe: TLabel;
mEin: TMemo; lmEin: TLabel; mAus: TMemo; lmAus: TLabel; bEnde: TButton;
procedure verarbeite_Eingabe(Sender: TObject);
procedure bEndeClick(Sender: TObject);
end;
var
Form1 : TForm1;
zustand : TZustand; { Zustand wird global gespeichert }
implementation
{$R *.DFM}
procedure TForm1.verarbeite_Eingabe(Sender : TObject);
var { Achtung, mit allen ButtonClicks verbinden !! }
eingabe : TEingabe;
ausgabe : TAusgabe;
begin
if Sender=be50 then eingabe := e50; { 5. Button <--> Eingabe zuordnen }
if Sender=be100 then eingabe := e100; { 5. Button <--> Eingabe zuordnen }
if Sender=beK then eingabe := eK; { 5. Button <--> Eingabe zuordnen }
if Sender=beW then eingabe := eW; { 5. Button <--> Eingabe zuordnen }
Form1.mEin.Lines.Add(eText[eingabe]); { zugehörigen Text ausgeben }
ausgabe := fa[zustand,eingabe]; { Ausgabe bestimmen }
eAusgabe.text := aText[ausgabe]; { zugehörigen Text ausgeben }
Form1.mAus.Lines.Add(aText[ausgabe]); { zugehörigen Text ausgeben }
zustand := fue[zustand,eingabe]; { neuen Zustand bestimmen }
eZustand.text := zText[zustand]; { zugehörigen Text ausgeben }
end;
procedure TForm1.bEndeClick(Sender: TObject);
begin
halt;
end;
initialization
zustand := z0; { 6. Startzustand setzen }
end.