Zitat von
SebE:
...[/delphi]
.. //KANN MAN DIE TABELLE "SCHÖNER" FÜLLEN?
[/delphi]
Ja.
Da es dem Automaten egal ist, ob es sich um eine 1, 2 usw. handelt, kann man das drastisch verkürzen: Der DEA kennt nur Ziffern, Komma, das Terminalsymbol (Ende der Eingabe) sowie ungültige Zeichen. Da ungültige Zeichen zu einem sofortigen Abruch führen, wird die Tabelle ziemlich klein. So klein, wie das Problem, das der DEA lösen soll:
Delphi-Quellcode:
Const
DEA : Array [symDigit..symTerminal, stStart..stDecimals] =
// symDigit , symKomma, symTerminal
{stStart } (stDigits , stError , stError),
{stDigits} (stDigits , stComma , stStop),
{stComma} (stDecimals, stError , stError),
{stDecimals}(stDecimals, stError , stStop)
);
// Natürlich implementiert man eine Funktion/Methode, die das nächste Symbol der Eingabe extrahiert,
// klassifiziert und sowohl Symbolklasse als auch das Symbol selbst zurückliefert.
So sehr Du an dem Case-Konstrukt zu hängen scheinst, es verstößt gegen diverse Grundregeln des 'clean coding' (DRY, KISS), weshalb ich das persönlich ablehne. Das ist aber Geschmackssache.
Ich stell mir nur gerade ein Case-Konstrukt mit 1700 Zuständen vor, bei dem nur bei 3 Zuständen etwas passiert...
Bei der Implementierung als Tabelle habe ich die Tabelle in einer Resource. Diese Tabelle wurde vorher vermutlich automatisch generiert und ist daher fehlerfrei. Die Implementierung selbst ist extrem kompakt und übersichtlich.
Zitat von
SebE:
Nicht dass ich nicht mit Klassen, Vererbung, etc. umgehen kann...erzeugt der StartZustand automatisch den "NextState" und das geht rekursiv immer so weiter bis StopZustand?
Äh..Nein.
Zitat von
SebE:
Ist das die "gute"
OOP-Lösung?
Imho schon.
Zitat von
SebE:
Ich finde diese unübersichtlich und fehleranfällig.
Nein. Jede Klasse, die das IAbstractState-Interface implementiert, MUSS Code für den Übergang zur nächsten Klasse implementieren, sowie den eigenen Zustand dokumentieren (IsStop, IsError). Spaghetti-Code ist hingegen unübersichtlich und fehleranfällig, da man sich jedesmal neu hineindenken muss und es ein leichtes ist, irgendwo einen Korken eingebaut zu haben (siehe unten)
Zitat von
himitsu:
ich würde es dann eher so machen...
Logisch, ich wollte nur den von mir einmal gemachten Fehler (im Codebeispiel für die Abarbeitung der Tabelle) nicht implizit korrigieren.
Zitat von
Reinhard Kern:
..in Assembler war das noch herrlich einfach ... war das schon fast selbstdokumentierend. Ausserdem fiel gleich auf, wenn das Programm unvollständig war, ... 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.
Siehe oben.
Zitat von
SebE:
Fehleranfällig in der Hinsicht, dass man nicht SOFORT erkennt, wer was wo erzeugt.
@SebE: Ich habe deinen Code abgeschrieben. Er funktioniert nicht. Wieso?
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'..'
8': fOutput := fOutput + fInput[fPos];
else fState := sStop
end
end;
fPos := fPos + 1
end
end;
function DEA_imperative._getOutput:
string;
begin
_getOutput := fOutput
end;
end.
Man muss sich in den kompletten Code eindenken und ALLES lesen, bis man den Fehler gefunden hat.