【问题标题】:FSM data structure designFSM数据结构设计
【发布时间】:2009-04-07 14:32:14
【问题描述】:

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

谢谢, syuga2012

【问题讨论】:

标签: fsm


【解决方案1】:

我们过去曾为电信公司实现过有限状态机,并且总是使用一组结构,预先填充如下:

/* 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++ 可能有更好的方法。

【讨论】:

  • 我们对我之前工作中使用的一些代码使用了非常相似的方法,使用起来非常棒。我想念它。
【解决方案2】:

有限状态机由有限数量的状态离散组成(我知道很迂腐,但仍然如此),通常可以表示为整数值。在 c 或 c++ 中,使用枚举是很常见的。

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

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

  1. 可能会转换到另一个状态
  2. 可能会产生一些输出

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,您可以将转换保存在数组中以进行快速索引。您还可以将函数指针保存在数组中以有效地处理动作阶段。这对于自动生成大型复杂状态机特别有用。

【讨论】:

    【解决方案3】:

    这里的答案看起来很复杂(但很准确)。所以这是我的想法。

    首先,我喜欢 dmckee 对 FSM 的(操作性)定义以及它们如何应用于编程。

    一个有限状态机由一个 有限数量的离散状态(我 知道迂腐,但仍然),这可以 一般用整数表示 价值观。在 c 或 c++ 中使用 枚举很常见。

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

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

    1. 可能会转换到另一个状态
    2. 可能会产生一些输出

    所以你有一个程序。它有状态,并且它们的数量是有限的。 (“灯泡亮”或“灯泡暗”或“灯泡熄灭。”3 种状态。有限。)您的程序一次只能处于一种状态。

    因此,假设您希望程序更改状态。通常,您会希望发生某些事情来触发状态更改。在这个例子中,我们如何通过用户输入来确定状态——比如按键。

    您可能需要这样的逻辑。当用户按键时:

    1. 如果灯泡“关闭”,则使灯泡“变暗”。
    2. 如果灯泡“暗”,则使灯泡“亮”。
    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 的工作原理。

    现在你可以做一些事情:

    1. 显然,这个程序只是改变了currentState 变量。您会希望您的代码在状态更改时做一些更有趣的事情。 doCoolBulbStuff() 函数可能,我不知道,实际上是在屏幕上放一张灯泡的图片。什么的。

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

      李>

    您的部分问题要求数据结构。

    有人建议使用enum 来跟踪状态。这是我在示例中使用的#defines 的一个很好的替代方案。人们也一直在建议使用数组——这些数组会跟踪状态之间的转换。这也是一个很好用的结构。

    鉴于上述情况,您可以使用任何类型的结构(类似树的东西、数组、任何东西)来跟踪各个状态并定义在每个状态下要做什么(因此有一些建议使用“函数指针” - 具有指向函数指针的状态映射,指示在该状态下要做什么。)

    希望有帮助!

    【讨论】:

      【解决方案4】:

      有关正式定义,请参阅Wikipedia。你需要决定你的状态集S、你的输入字母Σ和你的转换函数δ。最简单的表示是让 S 是整数 0、1、2、...、N-1 的集合,其中 N 是状态的数量,对于 Σ 是整数 0, 1, 2, ..., M-1 的集合,其中 M 是输入的数量,并且那么 δ 只是一个很大的 NM 矩阵。最后,您可以通过存储 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
      }
      

      【讨论】:

        【解决方案5】:

        您基本上可以使用“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){
                ...
            }
        }
        

        【讨论】:

          【解决方案6】:

          AT&T 的几个人(现在在 Google)编写了可供一般使用的最佳 FSM 库之一。看看这里,它叫OpenFST

          它快速、高效,并且他们创建了一组非常清晰的操作,您可以在 FSM 上执行这些操作,以执行诸如最小化它们或确定它们以使它们对现实世界问题更有用的事情。

          【讨论】:

            【解决方案7】:

            如果 FSM 是指有限状态机, 你喜欢它简单,使用枚举来命名你的状态 并在它们之间切换。

            否则使用函子。你可以看看 stl 或 boost 文档中的花哨定义。

            它们或多或少是对象,具有 方法例如调用run(),执行 在那种状态下应该做的一切, 每个州都有自己的优势 范围。

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 2013-02-13
              • 1970-01-01
              • 2015-06-17
              • 2017-04-10
              • 1970-01-01
              • 1970-01-01
              • 2015-12-24
              相关资源
              最近更新 更多