有限状态机(Python)
有限状态机(Finite-state machine, FSM),又称有限状态自动机,简称状态机,是表示有限个状态以及在这些状态之间的转移和动作等行为的数学模型。FSM是一种算法思想,简单而言,有限状态机由一组状态、一个初始状态、输入和根据输入及现有状态转换为下一个状态的转换函数组成。现实世界中存在大量具有有限个状态的系统:钟表系统、电梯系统、交通信号灯系统、通信协议系统、正则表达式、硬件电路系统设计、软件工程,编译器等,有限状态机的概念就是来自于现实世界中的这些有限系统。

一般可以用状态图来对一个状态机进行精确地描述。大家请看这个可乐机的状态图 。

从图中就可以清楚地看到可乐机的运行过程,图中直观地表现了可乐机投入不同金额硬币时的情况以及几个处理步骤的各个状态和它们之间的转换关系,根据投入硬币的不同面值,对总金额进行计算,并对各种操作进行响应以完成一次购买。 状态机的动态结构使得其在通讯系统,数字协议处理系统,控制系统,用户界面等领域得到了广泛地应用。
- 有限状态机模型
有限状态机是一个五元组$M=\left(Q,\Sigma ,\delta ,q_0,F\right)$,其中:
$Q=\left\{q_0,q_1,\text{...},q_n\right\}$是有限状态集合。在任一确定的时刻,有限状态机只能处于一个确定的状态$q_i$;
$\Sigma =\left\{\sigma _1,\sigma _{2,\text{...},}\sigma _n\right\}$是有限输入字符集合。在任一确定的时刻,有限状态机只能接收一个确定的输入$\sigma_j$;
$\delta :Q\times \Sigma \rightarrow Q$是状态转移函数,在某一状态下,给定输入后有限状态机将转入状态迁移函数决定的一个新状态;
$q_0\in Q$是初始状态,有限状态机由此状态开始接收输入;
$F\subseteq Q$是最终状态集合,有限状态机在达到终态后不再接收输入。
- 有限状态机的实现
有限状态机有多种实现方式:
1. switch-case或if-else
游戏引擎是有限状态机最为成功的应用领域之一,由于设计良好的状态机能够被用来取代部分的人工智能算法,因此游戏中的每个角色或者器件都有可能内嵌一个状态机。考虑RPG游戏中城门这样一个简单的对象,它具有打开(Opened)、关闭(Closed)、上锁(Locked)、解锁(Unlocked)四种状态。当玩家到达一个处于状态Locked的门时,如果此时他已经找到了用来开门的钥匙,那么他就可以利用它将门的当前状态转变为Unlocked,进一步还可以通过旋转门上的把手将其状态转变为Opened,从而成功地进入城内。

switch (state) {
// 处理状态Opened的分支
case (Opened): {
// 执行动作Open
open();
// 检查是否有CloseDoor事件
if (closeDoor()) {
// 当前状态转换为Closed
changeState(Closed)
}
break;
}
// 处理状态Closed的分支
case (Closed): {
// 执行动作Close
close();
// 检查是否有OpenDoor事件
if (openDoor()) {
// 当前状态转换为Opened
changeState(Opened);
}
// 检查是否有LockDoor事件
if (lockDoor()) {
// 当前状态转换为Locked
changeState(Locked);
}
break;
}
// 处理状态Locked的分支
case (Locked): {
// 执行动作Lock
lock();
// 检查是否有UnlockDoor事件
if (unlockDoor()) {
// 当前状态转换为Unlocked
changeState(Unlocked);
}
break;
}
// 处理状态Unlocked的分支
case (Unlocked): {
// 执行动作Unlock
unlock();
// 检查是否有LockDoor事件
if (lockDoor()) {
// 当前状态转换为Locked
changeState(Locked)
}
// 检查是否有OpenDoor事件
if (openDoor()) {
// 当前状态转换为Opened
changeSate(Opened);
}
break;
}
}
当状态量少并且各个状态之间变化的逻辑比较简单时,使用switch语句实现的有限状态机的确能够很好地工作,但代码的可读性并不十分理想。在很长一段时期内,使用switch语句一直是实现有限状态机的唯一方法,甚至像编译器这样复杂的软件系统,大部分也都直接采用这种实现方式。但之后随着状态机应用的逐渐深入,构造出来的状态机越来越复杂,这种方法也开始面临各种严峻的考验,其中最令人头痛的是如果状态机中的状态非常多,或者状态之间的转换关系异常复杂,那么简单地使用switch语句构造出来的状态机将难以扩展和维护。
2. 状态表
维护一个二维状态表,横坐标表示当前状态,纵坐标表示输入,表中一个元素存储下一个状态和对应的操作。这一招易于维护,但是运行时间和存储空间的代价较大。
3. 使用宏定义描述状态机
4. 面向对象的设计模式
一个简单的例子:我们想识别一句只包含有限个词语的话表达的语气。句子以"Python is"开头,后面接着一个形容词或是加not限定的形容词。例如,
"Python is great" → positive meaning
"Python is stupid" → negative meaning
"Python is not ugly" → positive meaning

首先定义一个StateMachine类
class StateMachine:
def __init__(self):
self.handlers = {} # 状态转移函数字典
self.startState = None # 初始状态
self.endStates = [] # 最终状态集合 # 参数name为状态名,handler为状态转移函数,end_state表明是否为最终状态
def add_state(self, name, handler, end_state=0):
name = name.upper() # 转换为大写
self.handlers[name] = handler
if end_state:
self.endStates.append(name) def set_start(self, name):
self.startState = name.upper() def run(self, cargo):
try:
handler = self.handlers[self.startState]
except:
raise InitializationError("must call .set_start() before .run()")
if not self.endStates:
raise InitializationError("at least one state must be an end_state") # 从Start状态开始进行处理
while True:
(newState, cargo) = handler(cargo) # 经过状态转移函数变换到新状态
if newState.upper() in self.endStates: # 如果跳到终止状态,则打印状态并结束循环
print("reached ", newState)
break
else: # 否则将转移函数切换为新状态下的转移函数
handler = self.handlers[newState.upper()]
然后自定义有限状态和状态转移函数,并在main函数中开始进行处理:
from statemachine import StateMachine # 有限状态集合
positive_adjectives = ["great","super", "fun", "entertaining", "easy"]
negative_adjectives = ["boring", "difficult", "ugly", "bad"] # 自定义状态转变函数
def start_transitions(txt):
# 过指定分隔符对字符串进行切片,默认为空格分割,参数num指定分割次数
# 将"Python is XXX"语句分割为"Python"和之后的"is XXX"
splitted_txt = txt.split(None, 1)
word, txt = splitted_txt if len(splitted_txt) > 1 else (txt,"")
if word == "Python":
newState = "Python_state" # 如果第一个词是Python则可转换到"Python状态"
else:
newState = "error_state" # 如果第一个词不是Python则进入终止状态
return (newState, txt) # 返回新状态和余下的语句txt def python_state_transitions(txt):
splitted_txt = txt.split(None,1)
word, txt = splitted_txt if len(splitted_txt) > 1 else (txt,"")
if word == "is":
newState = "is_state"
else:
newState = "error_state"
return (newState, txt) def is_state_transitions(txt):
splitted_txt = txt.split(None,1)
word, txt = splitted_txt if len(splitted_txt) > 1 else (txt,"")
if word == "not":
newState = "not_state"
elif word in positive_adjectives:
newState = "pos_state"
elif word in negative_adjectives:
newState = "neg_state"
else:
newState = "error_state"
return (newState, txt) def not_state_transitions(txt):
splitted_txt = txt.split(None,1)
word, txt = splitted_txt if len(splitted_txt) > 1 else (txt,"")
if word in positive_adjectives:
newState = "neg_state"
elif word in negative_adjectives:
newState = "pos_state"
else:
newState = "error_state"
return (newState, txt) if __name__== "__main__":
m = StateMachine()
m.add_state("Start", start_transitions) # 添加初始状态
m.add_state("Python_state", python_state_transitions)
m.add_state("is_state", is_state_transitions)
m.add_state("not_state", not_state_transitions)
m.add_state("neg_state", None, end_state=1) # 添加最终状态
m.add_state("pos_state", None, end_state=1)
m.add_state("error_state", None, end_state=1) m.set_start("Start") # 设置开始状态
m.run("Python is great")
m.run("Python is not fun")
m.run("Perl is ugly")
m.run("Pythoniseasy")
运行结果如下:
reached pos_state
reached neg_state
reached error_state
reached error_state
可以看到,这种有限状态机的写法,逻辑清晰,表达力强,有利于封装事件。一个对象的状态越多、发生的事件越多,就越适合采用有限状态机的写法。
transitions是一个由Python实现的轻量级的、面向对象的有限状态机框架。transitions最基本的用法如下,先自定义一个类,然后定义一系列状态和状态转移(定义状态和状态转移有多种方式,下面只写了最简明的一种,具体要参考文档说明),最后初始化状态机。

from transitions import Machine # 定义一个自己的类
class Matter(object):
pass
model = Matter() # 状态定义
states=['solid', 'liquid', 'gas', 'plasma'] # 定义状态转移
# The trigger argument defines the name of the new triggering method
transitions = [
{'trigger': 'melt', 'source': 'solid', 'dest': 'liquid' },
{'trigger': 'evaporate', 'source': 'liquid', 'dest': 'gas'},
{'trigger': 'sublimate', 'source': 'solid', 'dest': 'gas'},
{'trigger': 'ionize', 'source': 'gas', 'dest': 'plasma'}] # 初始化
machine = Machine(model=model, states=states, transitions=transitions, initial='solid') # Test
model.state # solid # 状体转变
model.melt() model.state # liquid
参考:
http://blog.csdn.net/xgbing/article/details/2784127
http://blog.csdn.net/gzlaiyonghao/article/details/1510688
http://www.python-course.eu/finite_state_machine.php
https://wiki.python.org/moin/FiniteStateMachine
https://github.com/tyarkoni/transitions
有限状态机(Python)的更多相关文章
- 【AMAD】transitions -- 一个python实现的轻量级,面向对象的有限状态机
简介 个人评分 简介 Transitions1是使用python实现的有限状态机2. 而有限状态机是实现经典模式 -- 状态模式3的前提. 这个库的API相当优雅,简洁. 另外博客园有人发布博客4介绍 ...
- python——有限状态机
前言 使用Python,大部分时间花在了处理文本上.在处理文本的时候,如果对有限状态机有所了解的话,处理起来会更加得心应手.可以把文本看成一个流,然后有一个机器对这个流进行操作.这个机器有状态,不同的 ...
- Python语言的有限状态机实现样例
#!/usr/bin/env python3 class Connection(object): def __init__(self): self.change_state(ClosedConnect ...
- [转]python 常用类库!
Python学习 On this page... (hide) 1. 基本安装 2. Python文档 2.1 推荐资源站点 2.2 其他参考资料 2.3 代码示例 3. 常用工具 3.1 Pytho ...
- 200行Python代码实现2048
200行Python代码实现2048 一.实验说明 1. 环境登录 无需密码自动登录,系统用户名shiyanlou 2. 环境介绍 本实验环境采用带桌面的Ubuntu Linux环境,实验中会用到桌面 ...
- 收集了一些python的文章
来自: 戴铭 2010-08-31 17:52:31 newthreading - safer concurrency for Python 安全并发(1回应) http://www.starming ...
- Python并行编程(十四):异步编程
1.基本概念 除了顺序执行和并行执行的模型以外,还有异步模型,这是事件驱动模型的基础.异步活动的执行模型可以只有一个单一的主控制流,能在单核心系统和多核心系统中运行. 在并发执行的异步模型中,许多任务 ...
- Atitit. 有限状态机 fsm 状态模式
Atitit. 有限状态机 fsm 状态模式 1. 有限状态机 1 2. "状态表"和"状态轮换表" 1 3. 有限状态机概念(状态(State)事件(Even ...
- Python入门 —— 2048实战(字符界面和图形界面)
2048 game (共4种实现方法) 目录: .. 图形界面 ... pygame 和 numpy .. 字符界面 ... 第一种 ... curses ... wxpython ... 第二种 . ...
随机推荐
- BizTalk开发系列(二十七) 异常管理中的数据编码
在BizTalk的异常管理解决方案中.大部分是通过订阅相关的升级属性来接收消息,并在自定义的流程或发送端口进行处理.但不管怎样,一般会定义统一的 错误消息Schema,这样不仅可以让我们通过异常信息快 ...
- php课程---建立一个简单的下拉列表框
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/ ...
- IOS第八天(7:UITableViewController新浪微博,cell 复用的简单写法优化和cell高度从模型中获取)
*********** #import "HMViewController.h" #import "HMStatus.h" #import "HMSt ...
- android NumberPicker 数组越界的坑
被这个问题耽误了一个多小时... 直接上解决方案,参考红色部分. private void initViews() { wheel = (NumberPicker) findViewById(R.id ...
- 模块加载----Webpack
一.配合gulp编译sass与压缩js 1.安装node.js 2.全局安装webpack 打开npm窗口执行 npm install webpack –g 3. 在项目中使用webpack 使用np ...
- Java 创建过滤器 解析xml文件
今天写了一个过滤器demo,现在是解析actions.xml文件,得到action中的业务规则:不需要导入任何jar包 ActionFilter过滤器类: package accp.com.xh.ut ...
- LightOj 1215 - Finding LCM(求LCM(x, y)=L中的 y )
题目链接:http://lightoj.com/volume_showproblem.php?problem=1215 题意:已知三个数a b c 的最小公倍数是 L ,现在告诉你 a b L 求最 ...
- win7 安装 memcached
1. 下载 memcached-win32-1.4.4-14.zip,里面包含6个文件,将解压后的文件夹随便放在什么位置.如果需要win64版,下载 memcached-win64-1.4.4-14. ...
- Gradle Cheat Sheet
加快编译速度 使用 gradle 2.4 及以上版本 ~/.gradle/gradle.properties 加入如下配置 org.gradle.daemon=true org.gradle.jvma ...
- IE10,IE11下cookie无法写入问题
asp.net 4.0的程序,发布后,测试在ie6,ie7,ie8,ie9下均可以正常登录,但是在ie10,ie11下就是无法保存cookie,排查了一下是否ie10,ie11是否存在设置问题发下并不 ...