Je cherche de bons exemples de machines à états finis; la langue n'est pas particulièrement importante, juste de bons exemples.
Les implémentations de code sont utiles (pseudo-code généralisé), mais il est également très utile de rassembler les différentes utilisations des FSM.
Les exemples n'ont pas nécessairement besoin d'être informatisés, par exemple l'exemple des réseaux ferroviaires de Mike Dunlavey, est très utile.
A Safe (événement déclenché)
feu de circulation (heure déclenchée | capteur [événement] déclenché)
Distributeur automatique (événement déclenché, une variation du sûr)
BGP est un protocole qui soutient les principales décisions de routage sur Internet. Il maintient une table pour déterminer l'accessibilité des hôtes à partir d'un nœud donné et a rendu Internet vraiment décentralisé.
Dans le réseau, chaque BGP nœud est un homologue et utilise une machine à états finis, avec l'un des six états Inactif , Connect, Active, OpenSent, OpenConfirm et Créé. Chaque connexion homologue du réseau conserve l'un de ces états.
Le protocole BGP détermine les messages qui sont envoyés aux pairs afin de changer leur état.
Le premier état Inactif. Dans cet état, BGP initialise les ressources, refuse les tentatives de connexion entrantes et établit une connexion avec l'homologue.
Le deuxième état Connect. Dans cet état, le routeur attend la fin de la connexion et passe à l'état OpenSent en cas de succès. En cas d'échec, il réinitialise le temporisateur ConnectRetry et passe à l'état Active à l'expiration.
Dans l'état Actif, le routeur remet à zéro le temporisateur ConnectRetry et revient à l'état Connect.
Dans l'état OpenSent, le routeur envoie un message Open et en attend un en retour. Les messages Keepalive sont échangés et, une fois la réception réussie, le routeur est placé dans l'état Established.
Dans l'état Established, le routeur peut envoyer/recevoir: Keepalive; Mise à jour; et des messages de notification vers/depuis son homologue.
Ils sont utiles pour modéliser toutes sortes de choses. Par exemple, un cycle électoral peut être modélisé avec des États sur le modèle de (gouvernement normal) - élection appelée -> (campagne précoce) - Parlement dissous -> (campagne intensive) - élection -> (dépouillement des votes ). Puis soit (dépouillement des votes) - pas de majorité -> (négociations de coalition) - accord atteint -> (gouvernement normal) ou (dépouillement des votes) - majorité -> (gouvernement normal). J'ai implémenté une variante de ce schéma dans un jeu avec un sous-jeu politique.
Ils sont également utilisés dans d'autres aspects des jeux: l'IA est souvent basée sur l'état; les transitions entre les menus et les niveaux et les transitions au décès ou au niveau terminé sont souvent bien modélisées par les FSM.
L'analyseur CSV utilisé dans le plug-in jquery-csv
C'est un Chomsky de base grammaire de type III analyseur.
Un tokenizer regex est utilisé pour évaluer les données caractère par caractère. Lorsqu'un caractère de contrôle est rencontré, le code est transmis à une instruction switch pour une évaluation plus approfondie basée sur l'état de départ. Les caractères non contrôlés sont regroupés et copiés en masse pour réduire le nombre d'opérations de copie de chaîne nécessaires.
Le tokenizer:
var tokenizer = /("|,|\n|\r|[^",\r\n]+)/;
Le premier jeu de correspondances est constitué des caractères de contrôle: séparateur de valeurs (") séparateur de valeurs (,) et séparateur d'entrée (toutes les variantes de la nouvelle ligne). La dernière correspondance gère le regroupement de caractères non contrôlés.
Il y a 10 règles que l'analyseur doit satisfaire:
Remarque: Les 7 principales règles sont dérivées directement de IETF RFC 418 . Les 3 derniers ont été ajoutés pour couvrir les cas Edge introduits par les applications de tableur modernes (ex Excel, Google Spreadsheet) qui ne délimitent pas (c.-à-d. Citent) toutes les valeurs par défaut. J'ai essayé de contribuer aux modifications du RFC mais je n'ai toujours pas entendu de réponse à ma demande.
Assez avec la liquidation, voici le diagramme:
États:
Transitions:
Remarque: il manque en fait un état. Il devrait y avoir une ligne de 'c' -> 'b' marquée avec l'état '1' car un deuxième délimiteur échappé signifie que le premier délimiteur est toujours ouvert. En fait, il serait probablement préférable de le représenter comme une autre transition. La création de ceux-ci est un art, il n'y a pas une seule manière correcte.
Remarque: il manque également un état de sortie mais sur des données valides, l'analyseur se termine toujours sur la transition 'a' et aucun des états n'est possible car il ne reste plus rien à analyser.
La différence entre les états et les transitions:
Un état est fini, ce qui signifie qu'il ne peut être déduit que pour signifier une chose.
Une transition représente le flux entre les états et peut donc signifier beaucoup de choses.
Fondamentalement, la relation état-> transition est 1 -> * (c'est-à-dire un à plusieurs). L'État définit "ce que c'est" et la transition définit "comment c'est géré".
Remarque: ne vous inquiétez pas si l'application des états/transitions ne semble pas intuitive, elle n'est pas intuitive. Il a fallu des correspondances approfondies avec quelqu'un de bien plus intelligent que moi avant de finalement avoir le concept.
Le pseudo-code:
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
}
}
Remarque: Ceci est l'essentiel, dans la pratique, il y a beaucoup plus à considérer. Par exemple, la vérification des erreurs, les valeurs nulles, une ligne vide de fin (c'est-à-dire qui est valide), etc.
Dans ce cas, l'état est l'état des choses lorsque le bloc de correspondance d'expression régulière termine une itération. La transition est représentée par les instructions case.
En tant qu'êtres humains, nous avons tendance à simplifier les opérations de bas niveau en des résumés de niveau supérieur, mais en travaillant avec un FSM c'est en travaillant avec des opérations de bas niveau. Bien que les états et les transitions soient très faciles à travailler individuellement, il est intrinsèquement difficile de visualiser le tout d'un coup. J'ai trouvé plus facile de suivre les chemins individuels d'exécution encore et encore jusqu'à ce que je puisse comprendre comment les transitions se déroulent. C'est comme apprendre des mathématiques de base, vous ne pourrez pas évaluer le code à partir d'un niveau supérieur jusqu'à ce que les détails de bas niveau deviennent automatiques.
A part: Si vous regardez l'implémentation réelle, il manque beaucoup de détails. Tout d'abord, tous les chemins impossibles lèveront des exceptions spécifiques. Il devrait être impossible de les toucher, mais si quelque chose se casse, ils déclencheront absolument des exceptions dans le lanceur de test. Deuxièmement, les règles de l'analyseur pour ce qui est autorisé dans une chaîne de données CSV `` légale '' sont assez lâches, donc le code nécessaire pour gérer un grand nombre de cas Edge spécifiques. Indépendamment de ce fait, c'était le processus utilisé pour se moquer du FSM avant toutes les corrections de bogues, extensions et réglages fins.
Comme avec la plupart des conceptions, ce n'est pas une représentation exacte de la mise en œuvre, mais elle décrit les parties importantes. Dans la pratique, il existe en fait 3 fonctions d'analyseur différentes dérivées de cette conception: un séparateur de ligne spécifique au csv, un analyseur à ligne unique et un analyseur à lignes multiples complet. Ils fonctionnent tous de manière similaire, ils diffèrent dans la façon dont ils traitent les caractères de nouvelle ligne.
J'ai trouvé que penser/modéliser un ascenseur (ascenseur) est un bon exemple de machine à états finis. Il nécessite peu d'introduction, mais offre une situation loin d'être triviale à mettre en œuvre.
Les états sont, par exemple, au rez-de-chaussée, au premier étage, etc., et se déplaçant du rez-de-chaussée, ou se déplaçant du troisième au rez-de-chaussée, mais actuellement entre les étages 3 et 2, etc.
L'effet des boutons dans la cage d'ascenseur et aux étages eux-mêmes fournit des entrées, dont l'effet dépend à la fois du bouton enfoncé et de l'état actuel.
Chaque étage, à l'exception du haut et du bas, aura deux boutons: l'un pour demander à l'ascenseur de monter, l'autre pour descendre.
FSM simple en 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;
}
}
Voilà. OK, ce n'est pas brillant, mais ça montre l'idée.
Vous trouverez souvent des FSM dans les produits de télécommunications car ils offrent une solution simple à une situation par ailleurs complexe.
OK, voici un exemple. Supposons que vous souhaitiez analyser un entier. Cela irait quelque chose comme dd*
où d
est un chiffre entier.
state0:
if (!isdigit(*p)) goto error;
p++;
goto state1;
state1:
if (!isdigit(*p)) goto success;
p++;
goto state1;
Bien sûr, comme l'a dit @Gary, vous pouvez masquer ces goto
s au moyen d'une instruction switch et d'une variable d'état. Notez que cela peut être structuré en ce code, qui est isomorphe à l'expression régulière d'origine:
if (isdigit(*p)){
p++;
while(isdigit(*p)){
p++;
}
// success
}
else {
// error
}
Bien sûr, vous pouvez également le faire avec une table de recherche.
Les machines à états finis peuvent être réalisées de plusieurs façons, et de nombreuses choses peuvent être décrites comme des instances de machines à états finis. Ce n'est pas tant une "chose" qu'un concept, pour penser aux choses.
Un exemple de FSM est un réseau ferroviaire.
Il existe un nombre fini de commutateurs où un train peut emprunter l'une des deux voies.
Il existe un nombre fini de pistes reliant ces commutateurs.
A tout moment, un train est sur une voie, il peut être envoyé sur une autre voie en traversant un aiguillage, sur la base d'un seul bit d'information d'entrée.
Machine à états finis en rubis:
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
C'est le comportement d'un nœud de calcul unique dans un système distribué, mettant en place un schéma de communication basé sur une liaison. Plus ou moins. Sous forme graphique, cela ressemble à ceci:
Consultez ce lien pour quelques exemples simples d'analyse lexicale (FSM):
http://ironbark.bendigo.latrobe.edu.au/subjects/SS/clect/clect03.html
Vous pouvez également consulter le "livre du dragon" pour des exemples (ce n'est pas une lecture légère)
http://en.wikipedia.org/wiki/Compilers:_Principles,_Techniques,_and_Tools
En pratique, les State Machines sont souvent utilisées pour:
Un exemple serait une machine d'état qui scanne une chaîne pour voir si elle a la bonne syntaxe. Les codes postaux néerlandais par exemple sont au format "1234 AB". La première partie ne peut contenir que des chiffres, la seconde uniquement des lettres. Une machine d'état peut être écrite pour savoir si elle est à l'état NUMBER ou à l'état LETTER et si elle rencontre une mauvaise entrée, la rejeter.
Cette machine d'état accepteur a deux états: numérique et alpha. La machine d'état démarre à l'état numérique et commence à lire les caractères de la chaîne à vérifier. Si des caractères non valides sont rencontrés pendant l'un des états, la fonction retourne avec une valeur False, rejetant l'entrée comme non valide.
Code 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)