![]() |
Re: Automaten in Source Code
Man könnte sogar schön objektorientiert arbeiten und für jeden Zustand eine Klasse definieren - damit erspart man sich ellenlange Case-Strukturen ;)
mfG Markus |
Re: Automaten in Source Code
Zitat:
|
Re: Automaten in Source Code
Zitat:
|
Re: Automaten in Source Code
Zitat:
Ist mein abstrakter Zustand vom Typ Basisklasse und jeder aktuelle (reale) Zusatnd dann von einer abgeleiteten Klasse? (das wären sehr viele speicheroperationen) Ich bitte um Aufklärung |
Re: Automaten in Source Code
Zitat:
Bei komplexeren (N)DEA würde ich eh zu einem Tool wie LEX/YACC greifen. Dann ist mir die interne Implementierung wurscht, und ich kann mich auf die Aktionen der Zustände konzentrieren. Zitat:
Delphi-Quellcode:
Jeder Zustand implementiert o.g. Interface. Fertig.
Type
IAbstractState = Interface Public Function NextState (Token : TSymbol) : IAbstractState; Procedure DoProcessState; Function IsStopState : Boolean; Function IsErrorState : Boolean; End; ... State := CoStartState.Create; While Not State.IsStopState Do Begin State := State.NextState; State.DoProcessState(); End; Zitat:
|
Re: Automaten in Source Code
Zitat:
![]() |
Re: Automaten in Source Code
Zitat:
Delphi-Quellcode:
so würde der StartState auch verarbeitet
State := CoStartState.Create;
While Not State.IsStopState Do Begin State.DoProcessState(); State := State.NextState; End; und im Fall eines StopStates würde die Schleife auch verlassen, bevor der StopState verarbeitet würde.
Delphi-Quellcode:
Type
IAbstractState = Interface Public Function NextState (Token : TSymbol) : IAbstractState; Procedure DoProcessState; Function IsStopState : Boolean; Function IsErrorState : Boolean; Function GetErrorText : WideString; End; State := CoStartState.Create; While Not State.IsStopState and Not State.IsErrorState Do Begin State.DoProcessState(); State := State.NextState; End; If State.IsErrorState Then ShowError(State.GetErrorText); |
Re: Automaten in Source Code
Hallo,
bei µP in Assembler war das noch herrlich einfach und klar: ein Zustandsautomat mit n Zuständen und m Ereignissen war softwaremässig eine n x m grosse Sprungtabelle. Bei geeigneter Namensgebung war das schon fast selbstdokumentierend. Ausserdem fiel gleich auf, wenn das Programm unvollständig war, weil im Zustand x für das Ereignis y keine geeignete Reaktion definiert war. Nun gibt es eigentlich in Pascal keine Sprünge, aber zusammenbauen könnte man sich so eine Tabelle schon und an jedem Schnittpunkt eine Prozedur für das Ereignis y im Zustand x eintragen. Natürlich ist das einer 2 fach verschachtelten Case-Struktur völlig gleichwertig. Aber wenn man das im Source-Code auch als n x m Tabelle schreiben kann, wird es sehr übersichtlich. Gruss Reinhard |
Re: Automaten in Source Code
So Leute, ich hab mal die "reine imperative" und die tabellengesteuerte Variante implementiert (haben beide auf Anhieb funktioniert ;-))
Achso: die Automaten lesen eine Kommazahl und ersetzten das "," zu "-".
Delphi-Quellcode:
unit Imperative;
interface type state = (sStart, sFrac, sTrunc, sStop); type DEA_imperative = class private fInput, fOutput: string; fPos: BYTE; fState: state; public constructor _create(const input: string); destructor _destroy; procedure _execute; function _getOutput: string; end; implementation constructor DEA_imperative._create(const input: string); begin fInput := input + #0 end; destructor DEA_imperative._destroy; begin end; procedure DEA_imperative._execute; begin fPos := 1; fState := sStart; fOutput := ''; while fState <> sStop do begin case fState of sStart: case fInput[fPos] of '1'..'9': begin fOutput := fInput[fPos]; fState := sTrunc end else fState := sStop end; sTrunc: case fInput[fPos] of '1'..'9': fOutput := fOutput + fInput[fPos]; ',': begin fOutput := fOutput + '-'; fState := sFrac end else fState := sStop end; sFrac: case fInput[fPos] of '1'..'9': fOutput := fOutput + fInput[fPos]; else fState := sStop end end; fPos := fPos + 1 end end; function DEA_imperative._getOutput: string; begin _getOutput := fOutput end; end.
Delphi-Quellcode:
Fazit:
unit TableControlled;
interface type state = (sStart, sTrunc, sTruncToFrac, sFrac, sStop); type DEA_tablecontrolled = class private fTable: array[sStart..sFrac] of array[CHAR] of state; fInput, fOutput: string; fPos: BYTE; fState: state; public constructor _create(const input: string); destructor _destroy; procedure _execute; function _getOutput: string; end; implementation constructor DEA_tablecontrolled._create(const input: string); var i: CHAR; begin fInput := input + #0; for i := #0 to #225 do begin fTable[sStart][i] := sStop; fTable[sTrunc][i] := sStop; fTable[sTruncToFrac][i] := sStop; fTable[sFrac][i] := sStop end; fTable[sStart]['0'] := sTrunc; //KANN MAN DIE TABELLE "SCHÖNER" FÜLLEN? fTable[sStart]['1'] := sTrunc; fTable[sStart]['2'] := sTrunc; fTable[sStart]['3'] := sTrunc; fTable[sStart]['4'] := sTrunc; fTable[sStart]['5'] := sTrunc; fTable[sStart]['6'] := sTrunc; fTable[sStart]['7'] := sTrunc; fTable[sStart]['8'] := sTrunc; fTable[sStart]['9'] := sTrunc; fTable[sTrunc]['0'] := sTrunc; fTable[sTrunc]['1'] := sTrunc; fTable[sTrunc]['2'] := sTrunc; fTable[sTrunc]['3'] := sTrunc; fTable[sTrunc]['4'] := sTrunc; fTable[sTrunc]['5'] := sTrunc; fTable[sTrunc]['6'] := sTrunc; fTable[sTrunc]['7'] := sTrunc; fTable[sTrunc]['8'] := sTrunc; fTable[sTrunc]['9'] := sTrunc; fTable[sTrunc][','] := sTruncToFrac; fTable[sTruncToFrac]['0'] := sFrac; fTable[sTruncToFrac]['1'] := sFrac; fTable[sTruncToFrac]['2'] := sFrac; fTable[sTruncToFrac]['3'] := sFrac; fTable[sTruncToFrac]['4'] := sFrac; fTable[sTruncToFrac]['5'] := sFrac; fTable[sTruncToFrac]['6'] := sFrac; fTable[sTruncToFrac]['7'] := sFrac; fTable[sTruncToFrac]['8'] := sFrac; fTable[sTruncToFrac]['9'] := sFrac; fTable[sFrac]['0'] := sFrac; fTable[sFrac]['1'] := sFrac; fTable[sFrac]['2'] := sFrac; fTable[sFrac]['3'] := sFrac; fTable[sFrac]['4'] := sFrac; fTable[sFrac]['5'] := sFrac; fTable[sFrac]['6'] := sFrac; fTable[sFrac]['7'] := sFrac; fTable[sFrac]['8'] := sFrac; fTable[sFrac]['9'] := sFrac end; destructor DEA_tablecontrolled._destroy; begin end; procedure DEA_tablecontrolled._execute; begin fPos := 1; fState := sStart; fOutput := ''; while fState <> sStop do begin fState := fTable[fState][fInput[fPos]]; case fState of //ich habe es mir gespart, die einzelnen Routinen in die Tabelle zu übernehmen sTrunc: fOutput := fOutput + fInput[fPos]; sTruncToFrac: fOutput := fOutput + '-'; sFrac: fOutput := fOutput + fInput[fPos] end; fPos := fPos + 1 end end; function DEA_tablecontrolled._getOutput: string; begin _getOutput := fOutput end; end. mit der Case-Variante beschreibt man die Zustandsaktion UND die Übergänge! => Weniger Zustände. => Meine Meinung: Case-Var. (in DIESEM Beispiel) besser. Zur OOP-Variante hab ich eine Frage: Nicht dass ich nicht mit Klassen, Vererbung, etc. umgehen kann, ich bring mir grad selbst die Konzepte bei (Probleme lösen mittels OOP) Wie erzeugt man am besten die Zustände? Im Beispiel steht:
Delphi-Quellcode:
erzeugt der StartZustand automatisch den "NextState" und das geht rekursiv immer so weiter bis StopZustand?
State := CoStartState.Create;
Ist das die "gute" OOP-Lösung? Ich finde diese unübersichtlich und fehleranfällig. Wie hat die OOP-Fraktion dieses Problem gelöst? |
Re: Automaten in Source Code
Zitat:
(*) Ich z.B. finde deine Code-Formatierung sehr unübersichtlich. |
Alle Zeitangaben in WEZ +1. Es ist jetzt 08:22 Uhr. |
Powered by vBulletin® Copyright ©2000 - 2025, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024-2025 by Thomas Breitkreuz