什么是有限状态自动机?

王大爷 2023年06月04日 615次浏览

FSM状态机

有限状态机在词法分析中的使用

概念 : 状态机是有限状态自动机(英语:finite-state machine,缩写:FSM)的简称,是表示有限个状态以及在这些状态之间的转移和动作等行为的数学模型。
概念 : 有限状态机是一种用来进行对象行为建模的工具,其作用主要是描述对象在它的生命周期内所经历的状态序列,以及如何响应来自外界的各种事件.
例子 : 举个最简单的例子,人有三个状态:健康,感冒,康复中。触发的条件有淋雨(t1),吃药(t2),打针(t3),休息(t4)。所以状态机就是健康-(t4)->健康;健康-(t1)->感冒;感冒-(t3)->健康;感冒-(t2)->康复中;康复中-(t4)->健康,等等。就是这样状态在不同的条件下跳转到自己或不同状态的图。
好处 : 应用FSM模型可以帮助对象生命周期的状态的顺序以及导致状态变化的事件进行管理。将状态和事件控制从不同的业务Service方法的if else中抽离出来。

为什么需要状态机

有限状态机是一种对象行为建模工具,适用对象有一个明确并且复杂的生命流(一般而言三个以上状态),并且在状态变迁存在不同的触发条件以及处理行为。使用状态机来管理对象生命流的好处更多体现在代码的可维护性、可测试性上,明确的状态条件、原子的响应动作、事件驱动迁移目标状态,对于流程复杂易变的业务场景能大大减轻维护和测试的难度。

状态机四要素

状态机可归纳为4个要素,即现态、条件、动作、次态。“现态”和“条件”是因,“动作”和“次态”是果。详解如下
现态:是指当前所处的状态 例如当前用户下了个单  , 还没付款 , 那么现态就是待付款。
条件:又称为“事件”。当一个条件被满足,将会触发一个动作,或者执行一次状态的迁移 , 例如一个订单用户付款这个动作 , 就是个事件 。
动作:条件满足后执行的动作。动作执行完毕后,可以迁移到新的状态,也可以仍旧保持原状态。动作不是必需的,当条件满足后,也可以不执行任何动作,直接迁移到新状态 , 例如 用户付完款  , 需要将订单状态改成待发货 , 该状态这个就是动作。
次态:条件满足后要迁往的新状态。“次态”是相对于“现态”而言的,“次态”一旦被激活,就转变成新的“现态”了 , 代发货就是次态 ,  付款 -> 触发修改状态动作 ->  现态由 "待付款" -> "待发货"。

1.地铁自动门状态机

![有限状态自动机(FSM)的一些逻辑_#define_02]image.png

2.一个简单的串口传输协议 "$"表示开始,"#” 表示结束

![有限状态自动机(FSM)的一些逻辑_debian_03]image.png

3.人形检测判断有没有人的状态机逻辑

![有限状态自动机(FSM)的一些逻辑_状态机_04]image.png

ATM机状态机

![有限状态自动机(FSM)的一些逻辑_#define_05]image.png

OTA的状态机

![有限状态自动机(FSM)的一些逻辑_debian_06]image.png

电梯状态机

![有限状态自动机(FSM)的一些逻辑_gnu_07]image.png


#include // printf #include // bool for _Bool and true for 1 #include "stdlib.h" // for rand() #include "math.h" /* event array and enum below must be in sync! */ enum state_codes { _idle=0, _goingUp=1, _goingDown=2, _AtTop=3, _AtBottom=4, _malfunction=5, _unexpected=6, _end=7 }; enum ret_codes { up=0, down=1, halt=2, top=3, bottom=4, fail=5, quit=6 }; enum state_codes x = _idle; int target_floor_number = 8; int current_floor_number = 1; int accumulated_floor_number = 0; #define TOP_FLOOR 9 #define BOTTOM_FLOOR 1 int event_idle(void){ printf("Idle!\n"); printf("Input target floor number in [1, 9] (which floor you want to go?): \n"); target_floor_number = getc(stdin) - '0'; if(current_floor_number < target_floor_number) return up; else if(current_floor_number > target_floor_number) return down; else if(current_floor_number == target_floor_number) return halt; } int event_goingUp(void){ current_floor_number += 1; accumulated_floor_number += 1; printf("Going up! Floor number is %d\n", current_floor_number); if(accumulated_floor_number > 100) return fail; else if(TOP_FLOOR == current_floor_number) return top; else if(BOTTOM_FLOOR == current_floor_number) return bottom; else if(current_floor_number < target_floor_number) return up; else if(current_floor_number == target_floor_number) return halt; else return quit; } int event_goingDown(void){ current_floor_number -= 1; accumulated_floor_number += 1; printf("Going down! Floor number is %d\n", current_floor_number); if(accumulated_floor_number > 100) return fail; else if(TOP_FLOOR == current_floor_number) return top; else if(BOTTOM_FLOOR == current_floor_number) return bottom; else if(current_floor_number > target_floor_number) return down; else if(current_floor_number == target_floor_number) return halt; else return quit; } int event_atTop(void){ printf("At top! current_floor_number= %d\n", current_floor_number); printf("Input target floor number in [1, 9] (which floor you want to go?): \n"); target_floor_number = getc(stdin) - '0'; if(current_floor_number > target_floor_number) return down; else if(current_floor_number == target_floor_number) return halt; } int event_atBottom(void){ printf("At Bottom! current_floor_number= %d\n", current_floor_number); printf("Input target floor number in [1, 9] (which floor you want to go?): \n"); target_floor_number = getc(stdin) - '0'; if(current_floor_number < target_floor_number) return up; else if(current_floor_number == target_floor_number) return halt;} int event_malfunction(void){ printf("Elevator needs maintanence!\n"); printf("accumulated_floor_number = %d\n", accumulated_floor_number); return quit; } int event_end(void){ printf("Exit!"); return 0; } int event_unexpected(void){ printf("Debug."); return quit; } int (* event[])(void) = { event_idle, event_goingUp, event_goingDown, event_atTop, event_atBottom, event_malfunction, event_unexpected, event_end }; // Nice, thanks for this. The only thing possible flaw here is that the lookup_transitions will likely be linear (O(n)) with this transition table datastructure. Its possible to make it better with multidimentional array - guaranteed O(1). Eg. the table can be represented as a multidimentional array where key is state and the value is an array where the key is the return code and value is the next state: int state_transitions[][3] = { [entry] = { foo, end, foo }, ... } /* ok, fail, repeat */ int lookup_transitions[][7] = { // return codes: // up down halt top bottom fail quit [_idle] = {_goingUp, _goingDown, _idle, _unexpected, _unexpected, _malfunction, _end}, [_goingUp] = {_goingUp, _unexpected, _idle, _AtTop, _AtBottom, _malfunction, _end}, [_goingDown] = {_unexpected, _goingDown, _idle, _AtTop, _AtBottom, _malfunction, _end}, [_AtTop] = {_unexpected, _goingDown, _AtTop, _unexpected, _unexpected, _malfunction, _end}, [_AtBottom] = {_goingUp, _goingDown, _AtBottom, _unexpected, _unexpected, _malfunction, _end}, [_malfunction] = {_end, _end, _end, _end, _end, _end, _end}, [_unexpected] = {_end, _end, _end, _end, _end, _end, _end} /* transitions from end state aren't needed */ }; #define ENTRY_STATE _idle #define END_STATE _end int main(void) { enum state_codes cur_state = ENTRY_STATE; enum ret_codes rc; int (* state_func)(void); printf("Start the elevator!\n"); for (;;) { state_func = event[cur_state]; rc = state_func(); if (END_STATE == cur_state) break; cur_state = lookup_transitions[cur_state][rc]; } printf("END."); getc(stdin); getc(stdin); getc(stdin); return 0; }

状态转移矩阵和状态操作函数,是建立状态机的的必要条件。