zoukankan      html  css  js  c++  java
  • 有限状态机FSM详解及其实现

    有限状态机,也称为FSM(Finite State Machine),其在任意时刻都处于有限状态集合中的某一状态。当其获得一个输入字符时,将从当前状态转换到另一个状态,或者仍然保持在当前状态。任何一个FSM都可以用状态转换图来描述,图中的节点表示FSM中的一个状态,有向加权边表示输入字符时状态的变化。如果图中不存在与当前状态与输入字符对应的有向边,则FSM将进入“消亡状态(Doom State)”,此后FSM将一直保持“消亡状态”。状态转换图中还有两个特殊状态:状态1称为“起始状态”,表示FSM的初始状态。状态6称为“结束状态”,表示成功识别了所输入的字符序列。

    在启动一个FSM时,首先必须将FSM置于“起始状态”,然后输入一系列字符,最终,FSM会到达“结束状态”或者“消亡状态”。

     

    说明:

    在通常的FSM模型中,一般还存在一个“接受状态”,并且FSM可以从“接受状态”转换到另一个状态,只有在识别最后一个字符后,才会根据最终状态来决定是否接受所输入的字符串。此外,也可以将“其实状态”也作为接受状态,因此空的输入序列也是可以接受的。

    FSM的实现

    程序设计思路大致如下:

    • 使用状态转换图描述FSM
    • 状态转换图中的结点对应不同的状态对象
    • 每个状态对象通过一个输入字符转换到另一个状态上,或者保持原状态不变。

    通过输入字符从一个状态切换到另一个状态的过程,我们称之为一个映射。在计算机程序设计中,我们可以有两种表示映射的方法:

    • 通过算法表示,即“可执行代码(Executable Code)”方式
    • 通过一张映射表,即“被动数据(Passive Data)”方式

    如下详细介绍这两种实现方式:

    • 通过Executable Code实现映射的FSM

    这种方式主要是通过条件分支来处理不同的字符,如if或者switch语句块,如

     1 State* State1::Transition(char c)
     2 {
     3     switch(c)
     4     {
     5     case 'A':
     6         return &s2;
     7     case 'B':
     8         return &s3;
     9     case 'C':
    10         return &s4;
    11     case 'D':
    12         return &s5;
    13     case '':
    14         return NULL;
    15     default:
    16         return NULL;
    17     }
    18 }
      1 // fsm_with_executable_code.h
      2 #ifndef FSM_WITH_EXECUTABLE_CODE_H
      3 #define FSM_WITH_EXECUTABLE_CODE_H
      4 
      5 #include <string.h>
      6 
      7 class State
      8 {
      9 public:
     10     virtual State* Transition(char c) = 0;
     11 };
     12 
     13 class Fsm
     14 {
     15 public:
     16     Fsm();
     17     void Reset();            // move to start state
     18     void Advance(char c);    // advance one transition
     19     int EndState();
     20     int DoomState();
     21 
     22 private:
     23     State* p_current;   // &s1, &s2, ..., &s6; NULL ==> doom
     24 };
     25 
     26 
     27 class State1 : public State
     28 {
     29 public:
     30     State* Transition(char c);
     31 };
     32 
     33 class State2 : public State
     34 {
     35 public:
     36     State* Transition(char c);
     37 };
     38 
     39 class State3 : public State
     40 {
     41 public:
     42     State* Transition(char c);
     43 };
     44 
     45 class State4 : public State
     46 {
     47 public:
     48     State* Transition(char c);
     49 };
     50 
     51 class State5 : public State
     52 {
     53 public:
     54     State* Transition(char c);
     55 };
     56 
     57 class State6 : public State
     58 {
     59 public:
     60     State* Transition(char c);
     61 };
     62 
     63 #endif // FSM_WITH_EXECUTABLE_CODE_H
     64 
     65 // fsm_with_executable_code.cc
     66 #include "fsm_with_executable_code.h"
     67 
     68 State1 s1;
     69 State2 s2;
     70 State3 s3;
     71 State4 s4;
     72 State5 s5;
     73 State6 s6;
     74 
     75 Fsm::Fsm()
     76 {
     77     p_current = NULL;
     78 }
     79 
     80 void Fsm::Reset()
     81 {
     82     p_current = &s1;
     83 }
     84 
     85 void Fsm::Advance(char c)
     86 {
     87     if (p_current != NULL)
     88         p_current = p_current->Transition(c);
     89 }
     90 
     91 int Fsm::EndState()
     92 {
     93     return p_current == &s6;
     94 }
     95 
     96 int Fsm::DoomState()
     97 {
     98     return p_current == NULL;
     99 }
    100 State* State1::Transition(char c)
    101 {
    102     switch(c)
    103     {
    104     case 'A':
    105         return &s2;
    106     case 'B':
    107         return &s3;
    108     case 'C':
    109         return &s4;
    110     case 'D':
    111         return &s5;
    112     case '':
    113         return NULL;
    114     default:
    115         return NULL;
    116     }
    117 }
    118 
    119 State* State2::Transition(char c)
    120 {
    121     switch(c)
    122     {
    123     case 'E':
    124         return &s2;
    125     case 'I':
    126         return &s6;
    127     case '':
    128         return NULL;
    129     default:
    130         return NULL;
    131     }
    132 }
    133 
    134 State* State3::Transition(char c)
    135 {
    136     switch(c)
    137     {
    138     case 'F':
    139         return &s3;
    140     case 'M':
    141         return &s4;
    142     case 'J':
    143         return &s6;
    144     case '':
    145         return NULL;
    146     default:
    147         return NULL;
    148     }
    149 }
    150 
    151 State* State4::Transition(char c)
    152 {
    153     switch(c)
    154     {
    155     case 'G':
    156         return &s4;
    157     case 'K':
    158         return &s6;
    159     case '':
    160         return NULL;
    161     default:
    162         return NULL;
    163     }
    164 }
    165 
    166 State* State5::Transition(char c)
    167 {
    168     switch(c)
    169     {
    170     case 'O':
    171         return &s2;
    172     case 'H':
    173         return &s5;
    174     case 'L':
    175         return &s6;
    176     case 'N':
    177         return &s4;
    178     case '':
    179         return NULL;
    180     default:
    181         return NULL;
    182     }
    183 }
    184 
    185 State* State6::Transition(char c)
    186 {
    187     return NULL;
    188 }
    189 
    190 // test_with_executable_code.cc
    191 #include "fsm_with_executable_code.h"
    192 
    193 #include "stdio.h"  // printf, scanf
    194 #include "stdlib.h" // system
    195 
    196 void test_fsm()
    197 {
    198     char input_string[80];
    199     printf("Enter input expression: ");
    200     scanf("%s", input_string);
    201 
    202     Fsm fsm;
    203     fsm.Reset();
    204     int index = 0;
    205     fsm.Advance(input_string[index++]);
    206 
    207     while (!fsm.EndState() && !fsm.DoomState())
    208         fsm.Advance(input_string[index++]);
    209 
    210     if (fsm.EndState())
    211         printf("
    Valid input expression");
    212     else
    213         printf("
    Invalid input expression");
    214 }
    215 
    216 int main()
    217 {
    218     test_fsm();
    219 
    220     system("pause");
    221 }
    View Code
    •  通过Passive Data实现映射的FSM

    在如上的switch分支中,其使用类型大致相同,因此,我们可以考虑将相似的信息保存到一张表中,这样就可以在程序中避免很多函数调用。在每个状态中都使用一张转换表来表示映射关系,转换表的索引使用输入字符来表示。此外,由于通过转换表就可以描述不同状态之间的变化,那么就没有必要将每种状态定义为一个类了,即不需要多余的继承和虚函数了,仅使用一个State即可。

    #include <limits.h>
    
    class State
    {
    public:
        State();
        State* transition[range];
    };
    
    对于任意一个状态state和输入字符c,后续状态都可以通过state.transition[c]来确定。
    
    类Fsm中的成员state包含6个状态,为了对应方便,我们将结束状态放在state[0]中,每个状态都使用一个三元组 { 当前状态,输入字符,下一个状态 } 来表示:
    
    struct TransGraph   // use triple to describe map
    {
        int current_state;
        char input_char;
        int next_state;
    };

    如此,使用了转换表代替了虚函数,简化了程序的设计。

      1 // fsm_with_passive_data.h
      2 #ifndef FSM_WITH_PASSIVE_DATA_H
      3 #define FSM_WITH_PASSIVE_DATA_H
      4 
      5 #include <string.h>
      6 #include <limits.h>     // CHAR_MAX
      7 
      8 const int range = CHAR_MAX + 1;
      9 
     10 class State
     11 {
     12 public:
     13     State();
     14     State* transition[range];
     15 };
     16 
     17 struct TransGraph   // use triple to describe map
     18 {
     19     int current_state;
     20     char input_char;
     21     int next_state;
     22 };
     23 
     24 class Fsm
     25 {
     26 public:
     27     Fsm();
     28     void Reset();            // move to start state
     29     void Advance(char c);    // advance one transition
     30     int EndState();
     31     int DoomState();
     32 
     33 private:
     34     State* p_current;   // &s1, &s2, ..., &s6; NULL ==> doom
     35     State state[6];     // 6 states, state[0] is end state
     36 };
     37 
     38 
     39 #endif // FSM_WITH_PASSIVE_DATA_H
     40 
     41 // fsm_with_passive_data.cc
     42 #include "fsm_with_passive_data.h"
     43 
     44 State::State()
     45 {
     46     for (int i = 0; i < range; ++i)
     47         transition[i] = NULL;
     48 }
     49 
     50 Fsm::Fsm()
     51 {
     52     static TransGraph graph[] =
     53     {
     54         {1, 'A', 2}, {1, 'B', 3}, {1, 'C', 4}, {1, 'D', 5},
     55         {2, 'E', 2}, {2, 'I', 0},
     56         {3, 'F', 3}, {3, 'J', 0}, {3, 'M', 4},
     57         {4, 'G', 4}, {4, 'K', 0},
     58         {5, 'H', 5}, {5, 'L', 0}, {5, 'O', 2}, {5, 'N', 4},
     59         {0, 0, 0}
     60     };
     61 
     62     for (TransGraph* p_tg = graph; p_tg->current_state != 0; ++p_tg)
     63         state[p_tg->current_state].transition[p_tg->input_char] = &state[p_tg->next_state];
     64 
     65     p_current = NULL;
     66 }
     67 
     68 void Fsm::Reset()
     69 {
     70     p_current = &state[1];
     71 }
     72 
     73 void Fsm::Advance(char c)
     74 {
     75     if (p_current != NULL)
     76         p_current = p_current->transition[c];
     77 }
     78 
     79 int Fsm::EndState()
     80 {
     81     return p_current == &state[0];
     82 }
     83 
     84 int Fsm::DoomState()
     85 {
     86     return p_current == NULL;
     87 }
     88 
     89 // test_with_passive_data.cc
     90 #include "fsm_with_passive_data.h"
     91 
     92 #include "stdio.h"  // printf, scanf
     93 #include "stdlib.h" // system
     94 
     95 void test_fsm()
     96 {
     97     char input_string[80];
     98     printf("Enter input expression: ");
     99     scanf("%s", input_string);
    100 
    101     Fsm fsm;
    102     fsm.Reset();
    103     int index = 0;
    104     fsm.Advance(input_string[index++]);
    105 
    106     while (!fsm.EndState() && !fsm.DoomState())
    107         fsm.Advance(input_string[index++]);
    108 
    109     if (fsm.EndState())
    110         printf("
    Valid input expression");
    111     else
    112         printf("
    Invalid input expression");
    113 }
    114 
    115 
    116 int main()
    117 {
    118     test_fsm();
    119 
    120     system("pause");
    121 }
    View Code

     

    通用FSM的设计

    如果类Fsm可以表示任意类型的FSM,那么就更符合程序设计的要求了。在构造函数中执行的具体配置应该被泛化为一种机制,我们通过这种机制来建立任意的FSM。在Fsm的构造函数中,应该将转换表作为一个参数传入,而非包含具体的转换表,如此,则不需要将转换表的大小硬编码到Fsm中了。因此,在构造函数中必须动态地创建这个存放转换表的内存空间,在析构函数中记着销毁这块内存。

     1 class Fsm
     2 {
     3 public:
     4     Fsm(TransGraph* p_tg);
     5     virtual ~Fsm();
     6     void Reset();
     7     void Advance(char c);
     8     int EndState();
     9     int DoomState();
    10 
    11 private:
    12     State* p_current;
    13     State* p_state;
    14 };
    15 
    16 Fsm::Fsm(TransGraph* p_tg)
    17 {
    18     int max_state = 0;  // size for dynamically allocated graph
    19     for (TransGraph* p_temp = p_tg; p_temp->current_state != 0; ++p_temp)
    20     {
    21         if (p_temp->current_state > max_state)
    22             max_state = p_temp->current_state;
    23         if (p_temp->next_state > max_state)
    24             max_state = p_temp->next_state;
    25     }
    26 
    27     p_state = new State[max_state + 1];
    28     for (TransGraph* p_temp = p_tg; p_temp->current_state != 0; ++p_temp)
    29         p_state[p_temp->current_state].transition[p_temp->input_char] = &p_state[p_temp->next_state];
    30 
    31     p_current = NULL;
    32 }
    33 
    34 Fsm::~Fsm()
    35 {
    36     delete []p_state;
    37 }
      1 // fsm_with_generalization.h
      2 #ifndef FSM_WITH_GENERALIZATION_H
      3 #define FSM_WITH_GENERALIZATION_H
      4 
      5 #include <string.h>
      6 #include <limits.h>     // CHAR_MAX
      7 
      8 const int range = CHAR_MAX + 1;
      9 
     10 class State
     11 {
     12 public:
     13     State();
     14     State* transition[range];
     15 };
     16 
     17 struct TransGraph
     18 {
     19     int current_state;
     20     char input_char;
     21     int next_state;
     22 };
     23 
     24 class Fsm
     25 {
     26 public:
     27     Fsm(TransGraph* p_tg);
     28     virtual ~Fsm();
     29     void Reset();
     30     void Advance(char c);
     31     int EndState();
     32     int DoomState();
     33 
     34 private:
     35     State* p_current;
     36     State* p_state;
     37 };
     38 
     39 
     40 #endif // FSM_WITH_GENERALIZATION_H
     41 
     42 // fsm_with_generalization.cc
     43 #include "fsm_with_generalization.h"
     44 
     45 State::State()
     46 {
     47     for (int i = 0; i < range; ++i)
     48         transition[i] = NULL;
     49 }
     50 
     51 Fsm::Fsm(TransGraph* p_tg)
     52 {
     53     int max_state = 0;  // size for dynamically allocated graph
     54     for (TransGraph* p_temp = p_tg; p_temp->current_state != 0; ++p_temp)
     55     {
     56         if (p_temp->current_state > max_state)
     57             max_state = p_temp->current_state;
     58         if (p_temp->next_state > max_state)
     59             max_state = p_temp->next_state;
     60     }
     61 
     62     p_state = new State[max_state + 1];
     63     for (TransGraph* p_temp = p_tg; p_temp->current_state != 0; ++p_temp)
     64         p_state[p_temp->current_state].transition[p_temp->input_char] = &p_state[p_temp->next_state];
     65 
     66     p_current = NULL;
     67 }
     68 
     69 Fsm::~Fsm()
     70 {
     71     delete []p_state;
     72 }
     73 
     74 void Fsm::Reset()
     75 {
     76     p_current = &p_state[1];
     77 }
     78 
     79 void Fsm::Advance(char c)
     80 {
     81     if (p_current != NULL)
     82         p_current = p_current->transition[c];
     83 }
     84 
     85 int Fsm::EndState()
     86 {
     87     return p_current == &p_state[0];
     88 }
     89 
     90 int Fsm::DoomState()
     91 {
     92     return p_current == NULL;
     93 }
     94 
     95 // test_with_generalization.cc
     96 #include "fsm_with_generalization.h"
     97 
     98 #include "stdio.h"  // printf, scanf
     99 #include "stdlib.h" // system
    100 
    101 void test_fsm()
    102 {
    103     char input_string[80];
    104     printf("Enter input expression: ");
    105     scanf("%s", input_string);
    106 
    107     TransGraph graph[] =
    108     {
    109         {1, 'A', 2}, {1, 'B', 3}, {1, 'C', 4}, {1, 'D', 5},
    110         {2, 'E', 2}, {2, 'I', 0},
    111         {3, 'F', 3}, {3, 'J', 0}, {3, 'M', 4},
    112         {4, 'G', 4}, {4, 'K', 0},
    113         {5, 'H', 5}, {5, 'L', 0}, {5, 'O', 2}, {5, 'N', 4},
    114         {0, 0, 0}
    115     };
    116 
    117     Fsm fsm(graph);
    118     fsm.Reset();
    119     int index = 0;
    120     fsm.Advance(input_string[index++]);
    121 
    122     while (!fsm.EndState() && !fsm.DoomState())
    123         fsm.Advance(input_string[index++]);
    124 
    125     if (fsm.EndState())
    126         printf("
    Valid input expression");
    127     else
    128         printf("
    Invalid input expression");
    129 }
    130 
    131 
    132 int main()
    133 {
    134     test_fsm();
    135 
    136     system("pause");
    137 }
    View Code

    当然也可以将上述程序中的转换表不放在主程序中,而是由一个派生自Fsm的子类SpecificFsm提供,在SpecificFsm中设置具体的转换表,然后通过SpecificFsm的初始化列表传到基类Fsm中,这样在主程序中就可以使用SpecificFsm来进行操作了。

  • 相关阅读:
    在GitHub上删除项目后,在Android Studio上传项目依然提示project is already on github
    Android Studio 使用Intent实现页面的跳转(带参数)
    Android Studio 点击两次返回键,退出APP
    Android Studio 使用ViewPager + Fragment实现滑动菜单Tab效果 --简易版
    Eclipse 分屏显示同一个代码文件
    关于线上问题处理心得分享
    关于敏捷开发
    Python语言程序设计学习 之 了解Python
    关于测试
    C# 使用FileUpload控件上传图片,将文件转换成二进制进行存储与读取
  • 原文地址:https://www.cnblogs.com/benxintuzi/p/4931258.html
Copyright © 2011-2022 走看看