什么是 FSM?

有限状态机(FSM) 是计算机科学中的一种数学模型,可用于表示和控制系统的行为。它由一组状态以及定义在这些状态上的转换函数组成。FSM 被广泛用于计算机程序中的状态机制。

说人话:只要有多种状态以及状态之间的转换函数,就能构成状态机。

比如灯也可算作是一个状态机,亮灯、暗灯分别是两种状态,转换函数是灯的开关,我们可以通过触发转换函数(即开关)对灯的状态进行改变。

那么,为什么要称作“有限”呢?因为在这种状态机下,所能表示的状态数是有限的,如红绿灯就只有红、绿、黄三种状态。

红绿灯
红绿灯

ATM
ATM

FSM 的应用场景多种多样,在红绿灯、地铁站的旋转闸门、编译器、Promise(包含三个状态:pending、resolved、rejected)等地方都有它的身影。

其实,人也是一种状态机,当给定刺激时(即触发转换函数时),人也会从一种状态变为另一种状态。比如我正高兴,突然你给了我一巴掌,那么我的状态就会从“高兴”转为“愤怒”。

FSM 原理

FSM 的核心概念包括:

  1. 状态集合(States)

    FSM 中所有可能状态的集合,如上面的电灯例子中,状态集合就是 [开, 关]

  2. 初始状态(Initial State)

    FSM 启动时的默认状态,如你将电灯接入电源后(即启动了这一状态机后),电灯的初始状态是

  3. 转换函数(Transition Function)

    定义了从一个状态转换为另一个状态的规则,转化过程由特定的输入触发,并根据当前状态决定下一个状态是什么

    转换函数可以被看作是一个映射,它接受当前状态和输入作为参数,并返回一个新的状态

    如:

    当前状态输入新状态
    按下开关
    按下开关
    def transition(current_state, action):
        if current_state == '关' and action == '按下开关':
            return '开'
        elif current_state == '开' and action == '按下开关':
            return '关'
        else:
            return current_state  # 如果输入不是预期的,则状态不变

    这个函数定义了如何根据当前状态和输入来切换电灯的状态

  4. 终止状态 / 接受状态(Final / Accept States)

    并不是所有的 FSM 都需要终止状态,但对于某些应用如自动机理论中的确定性有限自动机(DFA),终止状态指的是成功完成计算或识别过程后所达到的状态。一旦进入终止状态,机器可能会停止运行或者等待新的输入。

    电灯显然是一个无限循环系统(永远都可以在开关状态切换),因此,不需要设置终止状态。

值得注意的是,在设计状态机时,不仅可以定义状态之间的转换规则,还可以定义在进入或退出某个状态时要执行的行为,这些行为可能会对系统产生额外的影响(副作用,Side Effect),如我们现在需要在开关灯时打印出提示语句,就可以定义 on_enteron_exit 行为:

class LightSwitch:
    def __init__(self):
        self.state = '关'
    
    def on_enter(self, new_state):
        if new_state == '开':
            print("灯亮了")
        elif new_state == '关':
            print("灯灭了")
    
    def on_exit(self, old_state):
        print(f"离开了 {old_state} 状态")
    
    def transition(self, action):
        old_state = self.state
        if self.state == '关' and action == '按下开关':
            self.on_exit(old_state)
            self.state = '开'
            self.on_enter(self.state)
        elif self.state == '开' and action == '按下开关':
            self.on_exit(old_state)
            self.state = '关'
            self.on_enter(self.state)
        else:
            print("无效输入")

light_switch = LightSwitch()
light_switch.transition('按下开关')  # 输出: 离开了 关 状态\n灯亮了
light_switch.transition('按下开关')  # 输出: 离开了 开 状态\n灯灭了

Web 应用领域

在 Web 开发领域,什么情况下需要用到 FSM?一个常见的场景是词元拆分(Tokenization)

所有需要到词元拆分的场合,都可以使用有限状态机,如 Markdown 解析、编译器。下面我将用编译器举例:

在前端领域,在使用 Vite 构建项目时,打包过程使用了该机制:

  1. 词法分析

    首先要将源码拆分为一系列有意义的单元,这些单元称为“词元”(Tokens,和大预言模型中的词元概念一致,即不可拆分的最小单位,类似于化学中的原子)。如在 JS 中,词元可以是 functionlet

  2. 语法分析

    将这些 Tokens 变为 AST(抽象语法树),AST 是一种树形结构,表示了源代码的语法结构,这样,我们就可以对每一个节点进行处理,最终达到对源码整体修改的效果

  3. 将 AST 转为原生代码

    最后,再将这颗树上的各个节点遍历,修改为原生代码,从而实现编译

当然,实际上打包过程涉及到的技术细节比这要复杂的多得多

在后端领域,FSM 也可用于处理 TCP/IP 协议栈的状态转换、大模型返回的 Markdown 转 HTML 等……

总结

FSM 的应用广泛,在编程方面,大部分使用情况偏向底层,学会了 FSM 可以更好地了解主流框架的运作原理。

参考资料:

有限状态机 - 维基百科,自由的百科全书

前端 - 探索FSM (有限状态机)应用 - 个人文章 - SegmentFault 思否