FSM数据结构设计

时间:2009-04-07 14:32:14

标签: fsm

我想编写一个以空闲状态开始的FSM,并根据某个事件从一个状态移动到另一个状态。我不熟悉FSM的编码,谷歌没有帮助。 感谢是否有人可以发布可用于相同的C数据结构。

谢谢, syuga2012

7 个答案:

答案 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 ++中使用枚举是很常见的。

机器响应有限数量的输入,这些输入通常可以用另一个整数值变量表示。在更复杂的情况下,您可以使用结构来表示输入状态。

内部状态和外部输入的每种组合都会导致机器:

  1. 可能过渡到另一个州
  2. 可能生成一些输出
  3. 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 ++中使用   枚举很常见。

     

机器响应有限   经常可以输入的数量   用另一个整数表示   价值变量。更复杂   您可以使用结构的案例   表示输入状态。

     

内部状态和内部状态的每个组合   外部输入将导致机器   到:

     
      
  1. 可能过渡到另一个州
  2.   
  3. 可能生成一些输出
  4.   

所以你有一个程序。它有状态,并且数量有限。 (“灯泡很亮”或“灯泡昏暗”或“灯泡关闭。”3状态。有限。)你的程序一次只能处于一种状态。

所以,说你希望你的程序改变状态。通常,您需要发生以触发状态更改。在这个例子中,我们如何采取用户输入来确定状态 - 比如按键。

你可能想要这样的逻辑。当用户按下某个键时:

  1. 如果灯泡“关闭”,则将灯泡“变暗”。
  2. 如果灯泡“暗淡”,请将灯泡“亮”。
  3. 如果灯泡“亮”,请将灯泡“关闭”。
  4. 显然,不是“更换灯泡”,而是“改变文字颜色”或者你需要做的任何事情。在开始之前,您需要定义状态。

    所以看一些伪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的工作方式。

    现在,您可以执行以下操作:

    1. 显然,这个程序只是更改了currentState变量。您希望代码在状态更改时执行更有趣的操作。我不知道doCoolBulbStuff()函数可能会在屏幕上放置一个灯泡图片。或者别的什么。

    2. 此代码仅查找按键。但是你的FSM(以及你的switch语句)可以根据用户输入的内容选择状态(例如,“O”表示“关闭”,而不是仅仅按顺序进入下一步。)

    3. 您的部分问题要求提供数据结构。

      有人建议使用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(),执行 应该在那个州完成的一切, 每个州都拥有自己的优势 范围。