摘要:状态机模式是一种行为模式,在《设计模式》这本书中对其有详细的描述,通过多态实现不同状态的调转行为的确是一种很好的方法,只可惜在嵌入式环境下,有时只能写纯C代码,并且还需要考虑代码的重入和多任务请求跳转等情形,因此实现起来着实需要一番考虑。本文主要为你实现一个简单的有限状态机,没有考虑代码的重入和多任务跳转,为以后复杂的状态机实现,打下基础。

本文来源:用C语言实现有限状态自动机FSM

一、状态机实现的要素


首先,分析一下一个普通的状态机究竟要实现哪些内容。

状态机存储从开始时刻到现在的变化,并根据当前输入,决定下一个状态。这意味着,状态机要存储状态、获得输入(我们把它叫做跳转条件)、做出响应。

如上图所示,{s1, s2, s3}均为状态,箭头c1/a1表示在s1状态、输入为c1时,跳转到s2,并进行a1操作。

最下方为一组输入,状态机应做出如下反应:

当前状态 输入 下一个状态 动作
s1 c1 s2 a1
s2 c2 s3 a2
s3 c1 s2 a3
s2 c2 s3 a2
s3 c1 s2 a3
s2 c1 s_trap a_trap
s_trap c1 s_trap a_trap

当某个状态遇到不能识别的输入时,就默认进入陷阱状态,在陷阱状态中,不论遇到怎样的输入都不能跳出。

为了表达上面这个自动机,我们定义它们的状态和输入类型:

1
2
3
4
5
6
7
8
9
10
11
12
typedef 
int s
tate;
typedef 
int c
ondition;
 
#define STATES 4
#define STATE1 0
#define STATE2 1
#define STATE3 2
#define STATETRAP 3
 
#define CONDITIONS 2
#define CONDITION1 0
#define CONDITION2 1

总结一下,我们需要定义的有状态、输入、行为(动作+下一个状态),其中,行为的个数是“状态数*输入数量”(其中有一些是重复的);其中动作一般来说可以用一个函数指针来实现。

二、具体设计

在嵌入式环境中,由于存储空间比较小,因此把它们全部定义成宏。此外,为了降低执行时间的不确定性,我们使用O(1)的跳转表来模拟状态的跳转。

首先定义跳转类型:

1
2
3
4
5
6
7
typedef 
void 
(*actiontype)(state mystate, condition condition);
 
typedef 
struct
{
    s
tate next;
    a
ctiontype action;
} trasition, * ptrasition;

然后按照上图中的跳转关系,把三个跳转加一个陷阱跳转先定义出来:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// (s1, c1, s2, a1)
trasition t1 = {
    
STATE2,
    
action1
};
 
// (s2, c2, s3, a2)
trasition t2 = {
    
STATE3,
    
action2
};
 
// (s3, c1, s2, a3)
trasition t3 = {
    
STATE2,
    
action3
};
 
// (s, c, trap, a1)
trasition tt = {
    
STATETRAP,
    
actiontrap
};

其中的动作,由用户自己完成,在这里仅定义一条输出语句。

1
2
3
4
void 
action1(State state, Condition condition)
{
    
printf
(
"Action 1 triggered.\n"
);
}

1
最后定义跳转表:

1
2
3
4
5
6
7
pt
rasition transition_table[STATES][CONDITIONS] = {
/*      c1,  c2*/
/* s1 */
&t1, &tt,
/* s2 */
&tt, &t2,
/* s3 */
&t3, &tt,
/* st */
&tt, &tt,
};

即可表达上文中的跳转关系。

最后定义状态机,如果不考虑多任务请求,那么状态机仅需要存储当前状态便行了。例如:

1
2
3
4
5
6
7
8
9
10
11
12
typedef 
struct
{
    
State current;
} StateMachine, * pStateMachine;
 
State step(pStateMachine machine, Condition condition)
{
    
pTrasition t = transition_table[machine->current][condition];
    
(*(t->action))(machine->current, condition);
    
machine->current = t->next;
    
return 
machine->current;
}

总结:我们现在设计实现好了一个状态机,然后要给这个状态机特定的输入,看看状态机的运转情况,以上面图中的那个状态机为例,我们输入的序列是0和1分别代表c1和C2,然后状态s1,s2分别对应0,1.用程序实现这个内容如下


三、程序实现


程序清单:小型状态机的实现
#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>
typedef int state;
typedef int condition; #define STATENUM 4
#define STATE1 0
#define STATE2 1
#define STATE3 2
#define STATETRAP 3 #define CONDITIONS 2
#define CONDITION1 0
#define CONDITION2 1 typedef void (* actiontype)(state mystate,condition mycondition);
typedef struct{
state next;
actiontype action;
}trasition, *ptrasition; void action1(state mystate,condition myconditon);
void action2(state mystate,condition myconditon);
void action3(state mystate,condition myconditon);
void actiontrap(state mystate,condition myconditon);
trasition t1={
STATE2,action1
};
trasition t2={
STATE3,action2
};
trasition t3={
STATE2,action3
};
trasition tt={
STATETRAP,actiontrap
}; void action1(state mystate,condition myconditon){
printf("action1 one triggered\n");
}
void action2(state mystate,condition myconditon){
printf("action2 one triggered\n");
}
void action3(state mystate,condition myconditon){
printf("action3 one triggered\n");
}
void actiontrap(state mystate,condition myconditon){
printf("actiontrap one triggered\n");
} ptrasition transition_table[STATENUM][CONDITIONS] = {
/* c1, c2*/
/* s1 */&t1, &tt,
/* s2 */&tt, &t2,
/* s3 */&t3, &tt,
/* st */&tt, &tt,
};
typedef struct
{
state current;
} StateMachine, * pStateMachine; state step(pStateMachine machine, condition mycondition)
{
ptrasition t = transition_table[machine->current][mycondition];
(*(t->action))(machine->current, mycondition);
machine->current = t->next;
printf("the current state is %d\n",t->next );
return machine->current;
}
int main(int argc, char *argv[])
{
StateMachine mymachine;
mymachine.current=STATE1;
int mycon;
char ch;
while(1){
scanf("%d",&mycon);
step(&mymachine,mycon);
}
return 0;
}

程序输入与输出结果示例:



四、外部参考

用C语言实现有限状态自动机FSM的更多相关文章

  1. 非确定有限状态自动机的构建(一)——NFA的定义和实现

    保留版权,转载需注明出处(http://blog.csdn.net/panjunbiao). 非确定有限状态自动机(Nondeterministic Finite Automata,NFA)由以下元素 ...

  2. Trie 前缀树或字典树 确定有限状态自动机

    https://zh.wikipedia.org/wiki/Trie 应用 trie树常用于搜索提示.如当输入一个网址,可以自动搜索出可能的选择.当没有完全匹配的搜索结果,可以返回前缀最相似的可能.[ ...

  3. 简聊DFA(确定性有限状态自动机)

    状态机理论最初的发展在数字电路设计领域.而在软件设计领域,状态机设计的理论俨然已经自成一体. 状态机是软件编程中的一个重要概念,比这个概念更重要的是对它的灵活应用.在一个思路清晰而且高效的程序中,必然 ...

  4. 非确定有限状态自动机的构建(二)——将CharVal转换为NFA

    保留版权,转载注明出处:潘军彪的个人博客(http://blog.csdn.net/panjunbiao/article/details/9378933) 将上下文无关文法读入内存之后,可以将它转换成 ...

  5. 【BZOJ1212】L语言(AC自动机)

    [BZOJ1212]L语言(AC自动机) 题面 BZOJ 题解 很自然的,既然要匹配单词,那就全部都丢到\(AC\)自动机里面去 现在想想怎么匹配 先是\(AC\)自动机正常的匹配 如果此时这个位置能 ...

  6. DFA确定有限状态自动机

    DFA 在计算理论中,确定有限状态自动机或确定有限自动机(英语:deterministic finite automaton, DFA)是一个能实现状态转移的自动机.对于一个给定的属于该自动机的状态和 ...

  7. K:有限状态自动机

      有限状态自动机是一种特殊的状态机.它表示有限个状态以及在这些状态之间的转移和动作等行为的数学模型.有限状态自动机分为两种,一种是 确定有限状态自动机(DFA) ,一种是 非确定有限状态自动机(NF ...

  8. 【Codeforces 506E】Mr.Kitayuta’s Gift&&【BZOJ 4214】黄昏下的礼物 dp转有限状态自动机+矩阵乘法优化

    神题……胡乱讲述一下思维过程……首先,读懂题.然后,转化问题为构造一个长度为|T|+n的字符串,使其内含有T这个子序列.之后,想到一个简单的dp.由于是回文串,我们就增量构造半个回文串,设f(i,j, ...

  9. <轻量算法>根据核密度估计检测波峰算法 ---基于有限状态自动机和递归实现

    原创博客,转载请联系博主! 希望我思考问题的思路,也可以给大家一些启发或者反思! 问题背景: 现在我们的手上有一组没有明确规律,但是分布有明显聚簇现象的样本点,如下图所示: 图中数据集是显然是个3维的 ...

随机推荐

  1. 学无止境,学习AJAX(一)

    什么是AJAX?异步JavaScript和XML. AJAX是一种用于创建快速动态网页的技术. 通过在后台与服务器进行少量数据交换,AJAX 可以使网页实现异步更新.这意味着可以在不重新加载整个网页的 ...

  2. Oracle11g密码区分大小写导致database link无法连接

    http://f.dataguru.cn/thread-128013-1-1.html Oracle11g的密码默认是区分大小写的,该特性通过初始化参数sec_case_sensitive_logon ...

  3. HDU 5044 TREE

    题意:一棵树上两种操作,操作1,改变u到v的每一点的值增加k,操作2,改变u到v每一条边值增加k.最后结束时问,每一点和每一条边的值. 初始时,点和边的值都为0. 分析: 很显然要用树链剖分,将点和边 ...

  4. 轻松使用Nginx搭建web服务器

    如果读者以前做过web开发的话,就应该知道如何去搭建一个web服务器来跑你的web站点,在windows下你可能会选择去用IIS,十分的快捷,在linux下,你可能首先会想到apache,“一哥”( ...

  5. SPRING IN ACTION 第4版笔记-第四章ASPECT-ORIENTED SPRING-003-Spring对AOP支持情况的介绍

    一. 不同的Aop框架在支持aspect何时.如何织入到目标中是不一样的.如AspectJ和Jboss支持在构造函数和field被修改时织入,但spring不支持,spring只支持一般method的 ...

  6. javascript小游戏--生命游戏

    昨天参加Code Retreat的活动,"Code Retreat是一个一天的集中练习的活动,专注于软件开发和设计的基础". 要了解更多信息可前往 CodeRetreat官网 通过 ...

  7. 【原创】FPGA开发手记(二) VGA接口

    以下内容均以Xilinx的Nexys3作为开发板 1.VGA接口介绍 首先,先看电路图(3*5为例): 标准VGA一共15个接口,但是实际应用的接口信号只用五个:HSYNC,行同步信号:VSYNC,场 ...

  8. Oracle系列之触发器

    涉及到表的处理请参看原表结构与数据  Oracle建表插数据等等 创建一个触发器,使其可以修改tb_Department表的deptno. create or replace trigger upda ...

  9. Xcode 升级后, 插件无法使用的问题( PluginLoading: Required plug-in compatibility UUID.... )

    find ~/Library/Application\ Support/Developer/Shared/Xcode/Plug-ins -name Info.plist -maxdepth 3 | x ...

  10. 【转】was mutated while being enumerated 你是不是以为你真的懂For...in... ??

    原文网址:http://www.jianshu.com/p/ad80d9443a92 支持原创,如需转载, 请注明出处你是不是以为你真的懂For...in... ??哈哈哈哈, 我也碰到了这个报错 . ...