![]() |
Re: Automaten in Source Code
@NamenLozer:
Fehleranfällig in der Hinsicht, dass man nicht SOFORT erkennt, wer was wo erzeugt. Es ist besser, wenn die Zustände in der Gesamtheit von Anfang an "fest stehen", also existieren. Zu meinem Code: Wegen der BEGIN-END-Positionierung? Jedem das seine, das meinte ich aber nicht, hier ist nicht die Formatierung das Thema, sondern die Semantik! |
Re: Automaten in Source Code
Zitat:
|
Re: Automaten in Source Code
NamenLozer,
ist doch völlig Latte. :roll: Es entspricht so zwar nicht dem ![]() |
Re: Automaten in Source Code
Zitat:
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:
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.
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. Ich stell mir nur gerade ein Case-Konstrukt mit 1700 Zuständen vor, bei dem nur bei 3 Zuständen etwas passiert... :zwinker: 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:
Zitat:
Zitat:
Zitat:
Zitat:
Zitat:
Delphi-Quellcode:
Man muss sich in den kompletten Code eindenken und ALLES lesen, bis man den Fehler gefunden hat.
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. |
Re: Automaten in Source Code
Nachdem ich sowas gerade in ner Übung vorgesetzt bekommen habe, äußere ich mich auch mal :)
Für kleine Aufgaben reicht definitiv ein case-Statement. Rückblickend (mir ist diese Woche erst in den Sinn gekommen dass diese Zustandsautomaten existieren und Sinn machen) muss ich allerdings sagen dass ein case-Statement schwer wartbar ist. Für größere Sachen (die Grenze liegt je nach Geschmack woanders ... für mich vll. so bei 4x4 oder so) ist definitiv eine Tabelle von Vorteil. Die Tabelle kann man dann auch in einer Datei ablegen, damit erübrigt sich auch die Frage, wie man eine 10x10 Tabelle übersichtlich im Source deklariert ^^ Das was ich letztens gemacht habe war so ne Art Drag 'n Drop um mit ein paar grafischen Objekten zu spielen. Es lief dann darauf hinaus, dass ich ein "CurrentTool" (Typ: Enum) habe, und in den Events (MouseDown, MouseMove und MouseUp) jeweils ein Case-Statement in dem dann ein paar Sachen gemacht werden. (Das Tool kann nur über Buttons am Rand geändert werden, in den Maus-Events finden also keine Tool-Änderungen statt) Wenn man das mit so einer Tabelle machen würde, wären wahrscheinlich ganz schön viele Zustände dabei und ziemlich viele "Error-Zustände" - sobald ich den Überblick verliere werde ich das Programm entsprechend umgestalten ;) |
Re: Automaten in Source Code
@NamenLozer:
der Unterstrich macht mir deutlich, was MIR gehört und was vordefiniert (oder Compilermagic) ist. @alzaimar: Zitat:
Im Beispiel wurde nur der Startzustand erzeugt! -> Meine Frage lautete: erzeugt der Startzustand SEINEN NextState automatisch?
Delphi-Quellcode:
Wenn dem so WÄRE, DANN wäre es unübersichtlich.
StartZustantsInstanz := StartZustand.Create; //erzeugt von selbst zB Zustant2, den man mittels StartZustantsInstanz.GetNext verwenden kann
(entspricht nicht meinem Gedanken von Überschaulichkeit) Ich versteh nicht wieso man beim Case den gesamten Code durchdenken muss - es handelt sich immer um den gleichen Aufbau. (Zustand suchen, Eingabe suchen und ändern) Ich hab nichts gegen die Tabellenlösung (ab einer gewissen größe würde ich auch nicht auf diese Möglichkeit verzichten wollen), aber ICH finde, dass man sich die Tabelle bei evtl. Änderungen genauer ansehen müsste als "meine" Case. |
Re: Automaten in Source Code
Was mir grad noch auffällt:
Delphi-Quellcode:
Du musst deine Eingabe (wir gehen davon aus, dass wir sie als String bekommen) erst einmal in deine Token/Symbole umwandeln.
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) ); Spricht du brauchst einen Scanner/Lexer -> dann kommst du ja auch nicht um deine Case-Anweisung. Das war der Grund, warum meine Tabelle so riesig geworden ist (zwar groß, aber schnell). Es bleibt natürlich immernoch Geschmackssache. Ich für meinen Teil, bastle mir meine Parser (TopDown) ohne Generatoren -> Ich bin an die Cases gewöhnt. Die Tabellenlösung kommt aus der "BottomUp-Ecke". PS: die Quellcodes tun das, was ich von ihnen verlangt habe (Delphi 7 PE) |
Re: Automaten in Source Code
@_: Lustig ist, dass Delphi in der Unit System gerade die Funktionen die Compilermagic sind, mit einem vorangestellten _ definiert :) (Zumindest sagt mir mein Elefantenhirn das, dass dies mal vor ein paar zweistelligen Monaten gesehen hat... *hust*)
|
Re: Automaten in Source Code
Zitat:
Delphi-Quellcode:
Entsprechend befüllen, fertig. Dann einfach 'Symbol[CurrentChar]' nehmen und freuen, wie einfach das Leben sein kann.
Var
SymbolClass : Array [Char] Of TSymbol; Zitat:
Zitat:
Aber ich gebe zu, bei so keinen Geschichten ist ein Mini-DEA wirklich nett anzusehen (wenn man das nochmal sauberer hinbekommt) und ausreichend. Zitat:
Zitat:
Delphi-Quellcode:
Was macht denn diese Zeile?
State := State.NextState;
Zitat:
Zitat:
Zitat:
Mein Code ist wesentlich weniger fehleranfällig, schneller einsatzbereit (weil weniger Zeilen), wiederverwendbar, allgemeingültig, robust und leichter verständlich (Als DEA). Deiner nicht. In deinem Code ist es viel leichter möglich, einen Fehler einzubauen (siehe mein Beispiel). Er ist nicht für einen ähnlichen DEA verwendbar, sondern muss komplett neu geschrieben werden. Viel Spass, wenn der DEA auch Buchstaben oder Zahlen à la '1,234E+99' verarbeiten soll. Eine DEA-Tabelle ist natürlich kryptisch. Das stimmt. Bleib halt bei deinen CASE-Konstrukten und -vor allen Dingen- kodiere Parser, Scanner, Lexer und DEA per Hand. :zwinker: Zitat:
|
Re: Automaten in Source Code
Zitat:
Ob ich die Case durchsuchen muss oder die Tabelle neu "berechnen" muss - ist für mich der gleiche Aufwand! Zitat:
Delphi-Quellcode:
Man muss bei jeder Variante JEDEN Zusstandsübergang angeben/implementieren.
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) ); Wenn ich nun möchte, dass mein Automat ("0"en hab ich übrigens vergessen) nach dem Komma nur Ziffern "1".."8" akzeptieren soll, dann hab ich hier schon keinen Doppelten Code. Will sagen: es ist Zufall, dass hier doppelter Text steht. Wenn DU keine Fehler in der Tabelle machst, dann mache ICH keinen in meiner Case. Zitat:
NOCHMAL: Also werden alle Zustände "on demand" erzeugt(?). Wenn aktueller Zustand in den nächsten übergehen soll, erzeugt dieser (wenn nicht schon getan) den Neuen. WENN das die Lösung ist, geht mir hier OOP eindeutig zu weit (oder auch nicht(?)). Ich würde ein Objekt, welches die Tabelle an sich repräsentiert modellieren, die dann alle Zustände erzeugt. Sonst wären ja die Zustandsüberführungen über große Text-Entfernungen hinweg definiert. Es ist nicht die Aufgabe eines jeden Zustandes, sich um den nächsten zu kümmern. (MEINE Meinung) Zitat:
Zitat:
@Medium: Was interessiert es den Programmierer, wie die die interne Umsetzung von etwas aussieht? Ich GLAUBE, dass machen auch C-Compiler so (im Assembler-Code stehen dann "_" oder "__" vor allen Routinen) |
Alle Zeitangaben in WEZ +1. Es ist jetzt 04:19 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