我想编写一个以空闲状态开始的FSM,并根据某个事件从一个状态移动到另一个状态。我不熟悉FSM的编码,谷歌没有帮助。 感谢是否有人可以发布可用于相同的C数据结构。
谢谢, syuga2012
答案 0 :(得分:5)
我们过去为电信公司实施了有限状态机,并且总是使用预先填充的结构数组:
/* States */
#define ST_ANY 0
#define ST_START 1
: : : : :
/* Events */
#define EV_INIT 0
#define EV_ERROR 1
: : : : :
/* Rule functions */
int initialize(void) {
/* Initialize FSM here */
return ST_INIT_DONE
}
: : : : :
/* Structures for transition rules */
typedef struct {
int state;
int event;
(int)(*fn)();
} rule;
rule ruleset[] = {
{ST_START, EV_INIT, initialize},
: : : : :
{ST_ANY, EV_ERROR, error},
{ST_ANY, EV_ANY, fatal_fsm_error}
};
我可能将函数指针fn
声明为错误,因为这是来自内存。基本上,状态机在数组中搜索相关的状态和事件,并调用函数执行必须完成的操作然后返回新状态。
由于规则的优先级取决于它们在数组中的位置,因此首先放置特定状态并且ST_ANY条目持续存在。找到的第一条规则是使用的规则。
另外,我记得每个州的第一条规则都有一系列索引来加速搜索(所有具有相同起始状态的规则都被分组)。
还要记住,这是纯粹的C - 使用C ++可能有更好的方法。
答案 1 :(得分:2)
有限状态机由有限数量的离散状态组成(我知道迂腐,但仍然),通常可以表示为整数值。在c或c ++中使用枚举是很常见的。
机器响应有限数量的输入,这些输入通常可以用另一个整数值变量表示。在更复杂的情况下,您可以使用结构来表示输入状态。
内部状态和外部输入的每种组合都会导致机器:
c中的简单案例可能如下所示
enum state_val {
IDLE_STATE,
SOME_STATE,
...
STOP_STATE
}
//...
state_val state = IDLE_STATE
while (state != STOP_STATE){
int input = GetInput();
switch (state) {
case IDLE_STATE:
switch (input) {
case 0:
case 3: // note the fall-though here to handle multiple states
write(input); // no change of state
break;
case 1:
state = SOME_STATE;
break
case 2:
// ...
};
break;
case SOME_STATE:
switch (input) {
case 7:
// ...
};
break;
//...
};
};
// handle final output, clean up, whatever
虽然这很难阅读,但更容易通过以下方式分成多个功能:
while (state != STOP_STATE){
int input = GetInput();
switch (state) {
case IDLE_STATE:
state = DoIdleState(input);
break;
// ..
};
};
每个州的复杂性都在其自身的功能中。
作为m3rLinEz says,您可以在数组中保存转换以便快速编制索引。您还可以在数组中保存函数指针以有效地处理操作阶段。这对于自动生成大型复杂状态机特别有用。
答案 2 :(得分:2)
这里的答案看起来非常复杂(但仍然准确。)所以这是我的想法。
首先,我喜欢dmckee(操作)对FSM的定义以及它们如何应用于编程。
有限状态机由a组成 有限数的离散状态(I 知道迂腐,但仍然),哪个可以 通常表示为整数 值。在c或c ++中使用 枚举很常见。
机器响应有限 经常可以输入的数量 用另一个整数表示 价值变量。更复杂 您可以使用结构的案例 表示输入状态。
内部状态和内部状态的每个组合 外部输入将导致机器 到:
- 可能过渡到另一个州
- 可能生成一些输出
醇>
所以你有一个程序。它有状态,并且数量有限。 (“灯泡很亮”或“灯泡昏暗”或“灯泡关闭。”3状态。有限。)你的程序一次只能处于一种状态。
所以,说你希望你的程序改变状态。通常,您需要发生以触发状态更改。在这个例子中,我们如何采取用户输入来确定状态 - 比如按键。
你可能想要这样的逻辑。当用户按下某个键时:
显然,不是“更换灯泡”,而是“改变文字颜色”或者你需要做的任何事情。在开始之前,您需要定义状态。
所以看一些伪C代码:
/* We have 3 states. We can use constants to represent those states */
#define BULB_OFF 0
#define BULB_DIM 1
#define BULB_BRIGHT 2
/* And now we set the default state */
int currentState = BULB_OFF;
/* now we want to wait for the user's input. While we're waiting, we are "idle" */
while(1) {
waitForUserKeystroke(); /* Waiting for something to happen... */
/* Okay, the user has pressed a key. Now for our state machine */
switch(currentState) {
case BULB_OFF:
currentState = BULB_DIM;
break;
case BULB_DIM:
currentState = BULB_BRIGHT;
doCoolBulbStuff();
break;
case BULB_BRIGHT:
currentState = BULB_OFF;
break;
}
}
而且,瞧。一个改变状态的简单程序。
此代码仅执行switch
语句的一小部分 - 具体取决于当前状态。然后它更新该状态。这就是FSM的工作方式。
现在,您可以执行以下操作:
显然,这个程序只是更改了currentState
变量。您希望代码在状态更改时执行更有趣的操作。我不知道doCoolBulbStuff()
函数可能会在屏幕上放置一个灯泡图片。或者别的什么。
此代码仅查找按键。但是你的FSM(以及你的switch语句)可以根据用户输入的内容选择状态(例如,“O”表示“关闭”,而不是仅仅按顺序进入下一步。)
您的部分问题要求提供数据结构。
有人建议使用enum
来跟踪状态。这是我在我的示例中使用的#defines
的一个很好的替代方法。人们也建议使用数组 - 这些数组跟踪状态之间的转换。这也是一个很好的结构。
鉴于上述情况,你可以使用任何类型的结构(树状,数组,任何东西)来跟踪各个状态并定义每个状态下要做的事情(因此可以使用一些建议) “函数指针” - 有一个状态映射到一个函数指针,指示在该状态下该做什么。)
希望有所帮助!
答案 3 :(得分:1)
有关正式定义,请参阅Wikipedia。您需要确定状态集 S ,输入字母Σ和转换函数δ。最简单的表示是 S 是整数0,1,2,..., N -1的集合,其中 N 是状态数,Σ是整数0,1,2,..., M -1的集合,其中 M 是输入的数量,并且那么δ只是 M 矩阵的一个大 N 。最后,您可以通过存储 N 位数组来存储接受状态集,其中 i 位为1,如果 i state是接受状态,如果不是接受状态,则为0。
例如,这是维基百科文章图3中的FSM:
#define NSTATES 2
#define NINPUTS 2
const int transition_function[NSTATES][NINPUTS] = {{1, 0}, {0, 1}};
const int is_accepting_state[NSTATES] = {1, 0};
int main(void)
{
int current_state = 0; // initial state
while(has_more_input())
{
// advance to next state based on input
int input = get_next_input();
current_state = transition_function[current_state][input];
}
int accepted = is_accepting_state[current_state];
// do stuff
}
答案 4 :(得分:1)
您基本上可以使用“if”条件和变量来存储FSM的当前状态。
例如(只是一个概念):
int state = 0;
while((ch = getch()) != 'q'){
if(state == 0)
if(ch == '0')
state = 1;
else if(ch == '1')
state = 0;
else if(state == 1)
if(ch == '0')
state = 2;
else if(ch == '1')
state = 0;
else if(state == 2)
{
printf("detected two 0s\n");
break;
}
}
对于更复杂的实现,您可以考虑在二维数组中存储状态转换:
int t[][] = {{1,0},{2,0},{2,2}};
int state = 0;
while((ch = getch()) != 'q'){
state = t[state][ch - '0'];
if(state == 2){
...
}
}
答案 5 :(得分:1)
来自AT& T的一些人,现在在谷歌,编写了一个可用于一般用途的最好的FSM库。在这里查看,它被称为OpenFST。
它快速,高效,并且他们创建了一组非常清晰的操作,您可以在FSM上执行这些操作,例如最小化它们或确定它们以使它们对现实世界的问题更有用。
答案 6 :(得分:0)
如果通过FSM你的意思是有限状态机, 而且你喜欢它简单,使用枚举来命名你的状态 切换到他们之间。
否则使用仿函数。你可以看看 stl或boost docs中的花式定义。
它们或多或少都是对象,有一个 方法,例调用run(),执行 应该在那个州完成的一切, 每个州都拥有自己的优势 范围。