it-swarm.it

Esempi di macchine a stati finiti

Sto cercando buoni esempi di macchine a stati finiti; la lingua non è particolarmente importante, solo buoni esempi.

Le implementazioni di codice sono utili (pseudo-codice generalizzato), ma è anche molto utile per raccogliere i vari usi degli FSM.

Gli esempi non devono necessariamente essere basati su computer, ad esempio l'esempio delle reti ferroviarie di Mike Dunlavey è molto utile.

26
ocodo

A Safe (evento attivato)

  • Stati : più stati "bloccati", uno stato "sbloccato"
  • Transizioni : le combinazioni/i tasti corretti ti spostano dagli stati bloccati iniziali agli stati bloccati più vicini allo sbloccato, fino a quando non riesci finalmente a sbloccarlo. Combinazioni/chiavi errate ti riportano allo stato iniziale bloccato (a volte noto come inattivo .

Semaforo (tempo attivato | sensore [evento] attivato)

  • Stati : ROSSO, GIALLO, VERDE (esempio più semplice)
  • Transizioni : dopo un timer cambia ROSSO in VERDE, VERDE in GIALLO e GIALLO in ROSSO. Potrebbe anche essere innescato dal rilevamento di auto in vari stati (più complicati).

Distributore automatico (evento attivato, una variazione del sicuro)

  • Stati : IDLE, 5_CENTS, 10_CENTS, 15_CENTS, 20_CENTS, 25_CENTS, ecc., VEND, CHANGE
  • Transizioni : modifica dello stato al momento dell'inserimento di monete, banconote, transizione a VEND al momento dell'acquisto corretto (o più), quindi transizione a CHANGE o IDLE (a seconda quanto è etico il tuo distributore automatico)
28
aqua

Esempio di protocollo per gateway gateway

BGP è un protocollo che supporta le decisioni di routing di base su Internet. Mantiene una tabella per determinare la raggiungibilità degli host da un determinato nodo e ha reso Internet veramente decentralizzato.

Nella rete, ciascun nodo BGP è un peer e utilizza una macchina a stati finiti, con uno dei sei stati Inattivo , Connetti, Attivo, OpenSent, OpenConfirm e Fondata. Ogni connessione peer nella rete mantiene uno di questi stati.

Il protocollo BGP determina i messaggi inviati ai peer per modificarne lo stato.

Statistica BPG.

BGP statechart

Inattivo

Il primo stato Inattivo . In questo stato, BGP inizializza le risorse e rifiuta i tentativi di connessione in entrata e avvia una connessione al peer.

Collegare

Il secondo stato Connetti . In questo stato, il router attende il completamento della connessione e passa allo stato OpenSent se ha esito positivo. In caso di esito negativo, reimposta il timer ConnectRetry e passa allo stato Attivo alla scadenza.

Attivo

Nello stato Attivo , il router reimposta il timer ConnectRetry su zero e torna allo Connect stato.

OpenSent

Nello stato OpenSent , il router invia un messaggio Open e attende uno in cambio. I messaggi keepalive vengono scambiati e, una volta ricevuta correttamente, il router viene posto nello stato stabilito .

Stabilito

Nello stato Stabilito , il router può inviare/ricevere: Keepalive; Aggiornare; e messaggi di notifica da/verso il suo pari.

Altro le informazioni su BGP sono su wikipedia

14
ocodo

Sono utili per modellare tutti i tipi di cose. Ad esempio, un ciclo elettorale può essere modellato con stati sulla falsariga di (governo normale) - elezione chiamata -> (campagna elettorale anticipata) - Parlamento sciolto -> (campagna elettorale pesante) - elezione -> (conteggio dei voti ). Quindi (conteggio dei voti) - nessuna maggioranza -> (negoziati di coalizione) - accordo raggiunto -> (governo normale) o (conteggio dei voti) - maggioranza -> (governo normale). Ho implementato una variante di questo schema in un gioco con un gioco secondario politico.

Sono utilizzati anche in altri aspetti dei giochi: l'IA è spesso basata sullo stato; le transizioni tra menu e livelli e le transizioni in caso di morte o livello completato sono spesso ben modellate dagli FSM.

7
Peter Taylor

Il parser CSV utilizzato nel plug-in jquery-csv

È un parser di base Chomsky grammatica di tipo III .

Un tokenizer regex viene utilizzato per valutare i dati su base char-by-char. Quando viene rilevato un carattere di controllo, il codice viene passato a un'istruzione switch per un'ulteriore valutazione basata sullo stato iniziale. I caratteri non di controllo sono raggruppati e copiati in massa per ridurre il numero di operazioni di copia di stringhe necessarie.

Il tokenizer:

var tokenizer = /("|,|\n|\r|[^",\r\n]+)/;

Il primo set di corrispondenze sono i caratteri di controllo: delimitatore di valore (") separatore di valore (,) e separatore di voci (tutte le varianti di newline). L'ultima corrispondenza gestisce il raggruppamento di caratteri non di controllo.

Esistono 10 regole che il parser deve soddisfare:

  • Regola n. 1: una voce per riga, ogni riga termina con una nuova riga
  • Regola n. 2: trascinamento della nuova riga alla fine del file omesso
  • Regola n. 3: la prima riga contiene i dati dell'intestazione
  • Regola n. 4: gli spazi sono considerati dati e le voci non devono contenere una virgola finale
  • Regola n. 5: le righe possono essere o meno delimitate da virgolette doppie
  • Regola n. 6: i campi che contengono interruzioni di riga, virgolette doppie e virgole devono essere racchiusi tra virgolette doppie
  • Regola n. 7 - Se per racchiudere i campi vengono utilizzate le virgolette doppie, è necessario evitare una virgoletta doppia all'interno di un campo precedendola con un'altra virgoletta doppia
  • Emendamento n. 1 - Un campo non quotato può o può
  • Emendamento n. 2 - Un campo tra virgolette può o meno
  • Emendamento n. 3 - L'ultimo campo di una voce può contenere o meno un valore nullo

Nota: le prime 7 regole sono derivate direttamente da IETF RFC 418 . Gli ultimi 3 sono stati aggiunti per coprire i casi Edge introdotti dalle moderne app per fogli di calcolo (ex Excel, Google Spreadsheet) che non delimitano (ovvero citano) tutti i valori per impostazione predefinita. Ho provato a contribuire con le modifiche alla RFC ma non ho ancora ricevuto risposta alla mia richiesta.

Basta con il wind-up, ecco il diagramma:

CSV parser finite state machine

Uniti:

  1. stato iniziale per una voce e/o un valore
  2. è stata trovata una citazione di apertura
  3. è stata rilevata una seconda citazione
  4. è stato rilevato un valore non quotato

Transitions:

  • un. controlla sia i valori tra virgolette (1), i valori non quotati (3), i valori null (0), le voci null (0) e le nuove voci (0)
  • b. controlla un secondo preventivo char (2)
  • c. verifica la quotazione di escape (1), la fine del valore (0) e la fine della registrazione (0)
  • d. controlla la fine del valore (0) e la fine della voce (0)

Nota: in realtà manca uno stato. Dovrebbe esserci una linea da 'c' -> 'b' contrassegnata con lo stato '1' perché un secondo delimitatore con escape indica che il primo delimitatore è ancora aperto. In effetti, probabilmente sarebbe meglio rappresentarlo come un'altra transizione. Creare questi è un'arte, non esiste un solo modo corretto.

Nota: manca anche uno stato di uscita ma su dati validi il parser termina sempre sulla transizione 'a' e nessuno degli stati è possibile perché non è rimasto nulla da analizzare.

La differenza tra stati e transizioni:

Uno stato è finito, nel senso che può essere dedotto solo per significare una cosa.

Una transizione rappresenta il flusso tra gli stati, quindi può significare molte cose.

Fondamentalmente, la relazione stato-> transizione è 1 -> * (ovvero da uno a molti). Lo stato definisce "che cos'è" e la transizione definisce "come viene gestita".

Nota: non preoccuparti se l'applicazione di stati/transizioni non sembra intuitiva, non è intuitiva. Ci è voluto un po 'di corrispondenza con qualcuno molto più intelligente di me prima che finalmente riuscissi a mantenere il concetto.

Lo pseudo-codice:

csv = // csv input string

// init all state & data
state = 0
value = ""
entry = []
output = []

endOfValue() {
  entry.Push(value)
  value = ""
}

endOfEntry() {
  endOfValue()
  output.Push(entry)
  entry = []
}

tokenizer = /("|,|\n|\r|[^",\r\n]+)/gm

// using the match extension of string.replace. string.exec can also be used in a similar manner
csv.replace(tokenizer, function (match) {
  switch(state) {
    case 0:
      if(opening delimiter)
        state = 1
        break
      if(new-line)
        endOfEntry()
        state = 0
        break
      if(un-delimited data)
        value += match
        state = 3
        break
    case 1:
      if(second delimiter encountered)
        state = 2
        break
      if(non-control char data)
        value += match
        state = 1
        break
    case 2:
      if(escaped delimiter)
        state = 1
        break
      if(separator)
        endOfValue()
        state = 0
        break
      if(newline)
        endOfEntry()
        state = 0
        break
    case 3:
      if(separator)
        endOfValue()
        state = 0
        break
      if(newline)
        endOfEntry()
        state = 0
        break
  }
}

Nota: questo è il Gist, in pratica c'è molto altro da considerare. Ad esempio, controllo degli errori, valori null, una riga vuota finale (vale a dire che è valida), ecc.

In questo caso, lo stato è la condizione delle cose quando il blocco di corrispondenza regex termina un'iterazione. La transizione è rappresentata come le dichiarazioni del caso.

Come esseri umani, abbiamo la tendenza a semplificare le operazioni di basso livello in abstract di livello superiore ma lavorando con un FSM è lavorando con operazioni di basso livello. Mentre gli stati e le transizioni sono molto facili da lavorare individualmente, è intrinsecamente difficile visualizzare il tutto in una volta. Ho trovato più facile seguire ripetutamente i singoli percorsi di esecuzione fino a quando non ho potuto intuire come si svolgono le transizioni. È il re dell'apprendimento della matematica di base, non sarai in grado di valutare il codice da un livello superiore fino a quando i dettagli di basso livello iniziano a diventare automatici.

A parte: se si osserva l'implementazione effettiva, mancano molti dettagli. Innanzitutto, tutti i percorsi impossibili genereranno eccezioni specifiche. Dovrebbe essere impossibile colpirli, ma se qualcosa si rompe, scateneranno assolutamente eccezioni nel test runner. In secondo luogo, le regole del parser per ciò che è consentito in una stringa di dati CSV "legale" sono piuttosto vaghe, quindi il codice necessario per gestire molti casi Edge specifici. Indipendentemente da ciò, questo era il processo utilizzato per deridere l'FSM prima di tutte le correzioni di bug, estensioni e messa a punto.

Come con la maggior parte dei progetti, non è una rappresentazione esatta dell'implementazione ma delinea le parti importanti. In pratica, ci sono in realtà 3 diverse funzioni di parser derivate da questo progetto: uno splitter di linea specifico per CSV, un parser a linea singola e un parser a più righe completo. Operano tutti in modo simile, si differenziano per il modo in cui gestiscono i caratteri newline.

4
Evan Plaice

Ho scoperto che pensare/modellare un ascensore (ascensore) è un buon esempio di macchina a stati finiti. Richiede poco in termini di introduzione, ma fornisce una situazione tutt'altro che banale da implementare.

Gli stati si trovano, ad esempio, al piano terra, al primo piano ecc. E spostano il terreno al primo piano, oppure si spostano dal terzo al piano terra, ma attualmente tra il 3 ° e il 2 ° piano e così via.

L'effetto dei pulsanti nella gabbia dell'ascensore e ai piani stessi fornisce input, il cui effetto dipende sia dal pulsante che viene premuto insieme allo stato corrente.

Ogni piano, tranne quello superiore e quello inferiore, avrà due pulsanti: uno per richiedere l'ascensore per salire, l'altro per scendere.

3
jan

FSM semplice in Java

int i=0;

while (i<5) {
 switch(i) {
   case 0:
     System.out.println("State 0");
     i=1;
     break;
   case 1:
     System.out.println("State 1");
     i=6;
     break;
   default:
     System.out.println("Error - should not get here");
     break;      
  }

} 

Ecco qua OK, non è geniale, ma mostra l'idea.

Troverai spesso FSM nei prodotti di telecomunicazione perché offrono una soluzione semplice a una situazione altrimenti complessa.

3
Gary Rowe

OK, ecco un esempio. Supponiamo di voler analizzare un numero intero. Andrebbe qualcosa come dd* dove d è una cifra intera.

state0:
    if (!isdigit(*p)) goto error;
    p++;
    goto state1;
state1:
    if (!isdigit(*p)) goto success;
    p++;
    goto state1;

Naturalmente, come ha detto @Gary, potresti mascherare questi gotos mediante un'istruzione switch e una variabile di stato. Si noti che può essere strutturato in questo codice, che è isomorfo all'espressione regolare originale:

if (isdigit(*p)){
    p++;
    while(isdigit(*p)){
        p++;
    }
    // success
}
else {
    // error
}

Naturalmente puoi anche farlo con una tabella di ricerca.

Le macchine a stati finiti possono essere realizzate in molti modi e molte cose possono essere descritte come istanze di macchine a stati finiti. Non è una "cosa" tanto quanto un concetto, per pensare alle cose.

Esempio di rete ferroviaria

Un esempio di un FSM è una rete ferroviaria.

Esiste un numero finito di interruttori in cui un treno può andare su uno dei due binari.

Esiste un numero finito di tracce che collegano questi interruttori.

In qualsiasi momento, un treno si trova su un binario, può essere inviato a un altro binario attraversando un interruttore, basato su un singolo bit di informazioni di input.

2
Mike Dunlavey

Macchina a stati finiti in rubino:

module Dec_Acts
 def do_next
    @now = @next
    case @now
    when :invite
      choose_round_partner
      @next = :wait
    when :listen
      @next = :respond
    when :respond
      evaluate_invites
      @next = :update_in
    when :wait
      @next = :update_out
    when :update_in, :update_out
      update_edges
      clear_invites
      @next = :exchange
    when :exchange
      update_colors
      clear_invites
      @next = :choose
    when :choose
      reset_variables
      choose_role
    when :done
      @next = :done
    end
  end
end

Questo è il comportamento di un singolo nodo di calcolo in un sistema distribuito, configurando uno schema di comunicazione basato sul collegamento. Più o meno. In forma grafica sembra qualcosa del genere:

enter image description here

2
philosodad

Dai un'occhiata a questo link per alcuni semplici esempi di analisi lessicale (FSM):

http://ironbark.bendigo.latrobe.edu.au/subjects/SS/clect/clect03.html

Puoi anche dare un'occhiata al "libro dei draghi" per esempi (non è una lettura leggera)

http://en.wikipedia.org/wiki/Compilers:_Principles,_Techniques,_and_Tools

1
jmq

In pratica, le macchine statali vengono spesso utilizzate per:

  • Scopi di progettazione (modellizzazione delle diverse azioni in un programma)
  • Parser di linguaggio naturale (grammatica)
  • Analisi delle stringhe

Un esempio potrebbe essere una macchina a stati che esegue la scansione di una stringa per vedere se ha la sintassi corretta. I codici postali olandesi, ad esempio, sono formattati come "1234 AB". La prima parte può contenere solo numeri, la seconda solo lettere. È possibile scrivere una macchina a stati che tenga traccia del fatto se si trova nello stato NUMERO o nello stato LETTERA e se riscontra un input errato, rifiutarlo.

Questa macchina a stati accettore ha due stati: numerico e alfa. La macchina a stati si avvia nello stato numerico e inizia a leggere i caratteri della stringa da controllare. Se vengono rilevati caratteri non validi durante uno qualsiasi degli stati, la funzione restituisce un valore False, rifiutando l'input come non valido.

Codice Python:

import string

STATE_NUMERIC = 1
STATE_ALPHA = 2

CHAR_SPACE = " "

def validate_zipcode(s):
cur_state = STATE_NUMERIC

for char in s:
    if cur_state == STATE_NUMERIC:
        if char == CHAR_SPACE:
            cur_state = STATE_ALPHA
        Elif char not in string.digits:
            return False
    Elif cur_state == STATE_ALPHA:
        if char not in string.letters:
            return False
return True

zipcodes = [
    "3900 AB",
    "45D6 9A",
]

for zipcode in zipcodes:
    print zipcode, validate_zipcode(zipcode)

Fonte: macchine (finite-) statali in pratica

0
theD