什么是 FSM?
有限状态机(FSM) 是计算机科学中的一种数学模型,可用于表示和控制系统的行为。它由一组状态以及定义在这些状态上的转换函数组成。FSM 被广泛用于计算机程序中的状态机制。
说人话:只要有多种状态以及状态之间的转换函数,就能构成状态机。
比如灯也可算作是一个状态机,亮灯、暗灯分别是两种状态,转换函数是灯的开关,我们可以通过触发转换函数(即开关)对灯的状态进行改变。
那么,为什么要称作“有限”呢?因为在这种状态机下,所能表示的状态数是有限的,如红绿灯就只有红、绿、黄三种状态。
FSM 的应用场景多种多样,在红绿灯、地铁站的旋转闸门、编译器、Promise(包含三个状态:pending、resolved、rejected)等地方都有它的身影。
其实,人也是一种状态机,当给定刺激时(即触发转换函数时),人也会从一种状态变为另一种状态。比如我正高兴,突然你给了我一巴掌,那么我的状态就会从“高兴”转为“愤怒”。
FSM 原理
FSM 的核心概念包括:
状态集合(States)
FSM 中所有可能状态的集合,如上面的电灯例子中,状态集合就是
[开, 关]
初始状态(Initial State)
FSM 启动时的默认状态,如你将电灯接入电源后(即启动了这一状态机后),电灯的初始状态是
关
转换函数(Transition Function)
定义了从一个状态转换为另一个状态的规则,转化过程由特定的输入触发,并根据当前状态决定下一个状态是什么
转换函数可以被看作是一个映射,它接受当前状态和输入作为参数,并返回一个新的状态
如:
当前状态 输入 新状态 关 按下开关 开 开 按下开关 关 def transition(current_state, action): if current_state == '关' and action == '按下开关': return '开' elif current_state == '开' and action == '按下开关': return '关' else: return current_state # 如果输入不是预期的,则状态不变
这个函数定义了如何根据当前状态和输入来切换电灯的状态
终止状态 / 接受状态(Final / Accept States)
并不是所有的 FSM 都需要终止状态,但对于某些应用如自动机理论中的确定性有限自动机(DFA),终止状态指的是成功完成计算或识别过程后所达到的状态。一旦进入终止状态,机器可能会停止运行或者等待新的输入。
电灯显然是一个无限循环系统(永远都可以在开关状态切换),因此,不需要设置终止状态。
值得注意的是,在设计状态机时,不仅可以定义状态之间的转换规则,还可以定义在进入或退出某个状态时要执行的行为,这些行为可能会对系统产生额外的影响(副作用,Side Effect),如我们现在需要在开关灯时打印出提示语句,就可以定义 on_enter
和 on_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 构建项目时,打包过程使用了该机制:
词法分析
首先要将源码拆分为一系列有意义的单元,这些单元称为“词元”(Tokens,和大预言模型中的词元概念一致,即不可拆分的最小单位,类似于化学中的原子)。如在 JS 中,词元可以是
function
、let
等语法分析
将这些 Tokens 变为 AST(抽象语法树),AST 是一种树形结构,表示了源代码的语法结构,这样,我们就可以对每一个节点进行处理,最终达到对源码整体修改的效果
将 AST 转为原生代码
最后,再将这颗树上的各个节点遍历,修改为原生代码,从而实现编译
当然,实际上打包过程涉及到的技术细节比这要复杂的多得多
在后端领域,FSM 也可用于处理 TCP/IP 协议栈的状态转换、大模型返回的 Markdown 转 HTML 等……
总结
FSM 的应用广泛,在编程方面,大部分使用情况偏向底层,学会了 FSM 可以更好地了解主流框架的运作原理。
参考资料:
文章的叙述风格独特,用词精准,让人回味无穷。
存在主义视角的介入提升了思想维度。