Python——有限状态机

news2024/11/15 13:43:17

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

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

  从图中就可以清楚地看到可乐机的运行过程,图中直观地表现了可乐机投入不同金额硬币时的情况以及几个处理步骤的各个状态和它们之间的转换关系,根据投入硬币的不同面值,对总金额进行计算,并对各种操作进行响应以完成一次购买。 状态机的动态结构使得其在通讯系统,数字协议处理系统,控制系统,用户界面等领域得到了广泛地应用。

  • 有限状态机模型

有限状态机是一个五元组M=(Q,Σ,δ,q0,F)M=(Q,Σ,δ,q0,F),其中:

Q={q0,q1,...,qn}Q={q0,q1,...,qn}是有限状态集合。在任一确定的时刻,有限状态机只能处于一个确定的状态qiqi;

Σ={σ1,σ2,...,σn}Σ={σ1,σ2,...,σn}是有限输入字符集合。在任一确定的时刻,有限状态机只能接收一个确定的输入σjσj;

δ:Q×Σ→Qδ:Q×Σ→Q是状态转移函数,在某一状态下,给定输入后有限状态机将转入状态迁移函数决定的一个新状态;

q0∈Qq0∈Q是初始状态,有限状态机由此状态开始接收输入;

F⊆QF⊆Q是最终状态集合,有限状态机在达到终态后不再接收输入。

  • 有限状态机的实现

  有限状态机有多种实现方式:

  1. switch-case或if-else

  游戏引擎是有限状态机最为成功的应用领域之一,由于设计良好的状态机能够被用来取代部分的人工智能算法,因此游戏中的每个角色或者器件都有可能内嵌一个状态机。考虑RPG游戏中城门这样一个简单的对象,它具有打开(Opened)、关闭(Closed)、上锁(Locked)、解锁(Unlocked)四种状态。当玩家到达一个处于状态Locked的门时,如果此时他已经找到了用来开门的钥匙,那么他就可以利用它将门的当前状态转变为Unlocked,进一步还可以通过旋转门上的把手将其状态转变为Opened,从而成功地进入城内。

 View Code

  当状态量少并且各个状态之间变化的逻辑比较简单时,使用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开源库

  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://www.coloradmin.cn/o/78850.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【Lilishop商城】No3-5.模块详细设计,商品模块-1(商品分类、品牌管理、规格管理、参数、计量单位、店铺分类)的详细设计

仅涉及后端,全部目录看顶部专栏,代码、文档、接口路径在: 【Lilishop商城】记录一下B2B2C商城系统学习笔记~_清晨敲代码的博客-CSDN博客 全篇会结合业务介绍重点设计逻辑,其中重点包括接口类、业务类,具体的结合源代码…

人脸特征点检测入门

基础 人脸特征点可以用来做脸型、眼睛形状、鼻子形状等分析,从而对人脸的特定位置进行修饰加工,实现人脸的特效美颜。人脸识别等算法可以通过对人脸的姿态进行对齐从而提高模型的精度。 68点标注是现今最通用的一种标注方案,早期在1999年的…

Effective C++条款30:透彻了解inlining的里里外外(Understand the ins and outs of inlining)

Effective C条款30:透彻了解inlining的里里外外(Understand the ins and outs of inlining)条款30:透彻了解inlining的里里外外1、inline函数的优缺点2、隐式内联和显式内联2.1 隐式内联2.2 显式内联3、函数模板必须inline么&…

自动驾驶之3D点云聚类算法调研

1. 方法 总共分为4类 基于欧式距离的聚类Supervoxel 聚类深度(Depth) 聚类Scanline Run 聚类 1.1 基于欧氏距离的聚类 思路: 在点云上构造kd-tree, 然后在某个半径阈值(例如0.5m), 则分割为一个实例。 相似算法: RBNN (radially bounded nearest neighbor graph), 2008. …

在 Ubuntu 上安装 Discourse 开发环境

本指南只针对 Discourse 开发环境的配置,如果你需要在生产环境中安装 Discourse ,请访问页面:Install Discourse in production with the official, supported instructions - sysadmin - Discourse Meta 中的内容。 有关开发环境的设置英文原…

[Java EE初阶] 进程调度的基本过程

纪念Java EE初阶开篇文章,不放弃,不摆烂,踏平所有障碍吧!少年!奥利给!(操作系统这方面的所有文章均不作为操作系统的专业课知识学习) 文章目录1. 进程的概念2. PCB --- 进程控制块3. 并发与并行4. 进程调度的相关属性5. 内存管理总结1. 进程的概念 进程,就是跑起来的程序,我们…

【学习笔记】《Python深度学习》第七章:高级的深度学习最佳实践

文章目录1 Keras 函数式 API1.1 函数式 API 简介1.2 多输入模型1.3 多输出模型1.4 层组成的有向无环图1.5 共享层权重1.6 将模型作为层2 使用 Keras 回调函数 和 TensorBoard 检查并监控深度学习模型2.1 训练过程中将回调函数作用于模型2.2 TensorBoard 简介:Tensor…

【Lilishop商城】No3-4.模块详细设计,店铺店员(店铺店员、店铺部门、店铺角色)的详细设计

仅涉及后端,全部目录看顶部专栏,代码、文档、接口路径在: 【Lilishop商城】记录一下B2B2C商城系统学习笔记~_清晨敲代码的博客-CSDN博客 全篇会结合业务介绍重点设计逻辑,其中重点包括接口类、业务类,具体的结合源代码…

exfat文件系统

DBR: DBR偏移量 字段长度(字节) 说明 0x40 - 0x47 8 分区的起始扇区号(隐藏扇区数) 0x48 - 0x4F 8 分区总扇区数 0x50 - 0x53 4 FAT表起始扇区号(从DBR到FAT表的扇区个数) 0x54 - 0x57 4…

【Redis】持久化操作

一、RDB(Redis Database) 1、持久化 redis一般是将数据写到内存中,但也可以将数据写到磁盘中,这个过程称之为持久化 2、什么是RDB 在指定的时间间隔内将内存中的数据集快照写入磁盘中 3、RDB是如何执行备份操作的 redis会单独创建(fork)一个子进程进行…

FPGA 20个例程篇:18.SD卡存放音频WAV播放(下)

第七章 实战项目提升,完善简历 18.SD卡存放音频WAV播放(下) 进一步地我们再结合图1的示意图来分析wav_play模块的时序逻辑设计,大家可以清楚地看到WM8731在Right justified和主从时钟模式下,是先发左声道后发右声道数…

【LeetCode】专题一 二叉树层序遍历

二叉树层序遍历 在本文中,我将会选取LeetCode上二叉树层序遍历的多道例题,并给出解答,通过多道题我们就可以发现,二叉树的层序遍历并不复杂,并且有着共通点。 102. 二叉树的层序遍历 给你二叉树的根节点 root &…

【Labivew】简易计算器

🚩write in front🚩 🔎大家好,我是謓泽,希望你看完之后,能对你有所帮助,不足请指正!共同学习交流🔎 🏅2021年度博客之星物联网与嵌入式开发TOP5&#xff5…

Secure CRT远程连接很快断线问题

问题描述 我们使用Secure CRT连接远程主机时可能会遇到几分钟没操作就无法操作了,需要断开重新连接,非常的麻烦,假如客户端或者服务端能够在快要超时的时候给对方发送一个心跳,得到对方响应就重置下超时时间,这样就能…

arm架构 --- 中断

ARM的异常 终止程序的正常执行过程而不得不去完成的一些特殊工作 中断是异常的一种,包括外部硬件产生的异常和芯片内部硬件产生的内部中断。 ARM有七种处理器模式,其中用户模式和系统模式之外的5钟处理器模式叫做异常模式,用户模式之外的6…

osgEarth示例分析——osgearth_terrainprofile

前言 osgearth_terrainprofile示例,涉及到一个新的类 TerrainProfileCalculator(地形轮廓计算器类),用来计算两个点连线之间的地形数据。左下角会根据点击的起点和终点进行计算,并更新显示地形信息。 效果 拖动地球,到某一个视…

[附源码]Python计算机毕业设计SSM基于的智慧校园安防综合管理系统(程序+LW)

项目运行 环境配置: Jdk1.8 Tomcat7.0 Mysql HBuilderX(Webstorm也行) Eclispe(IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持)。 项目技术: SSM mybatis Maven Vue 等等组成,B/S模式 M…

软件安全测试-Web安全测试详解-XSS攻击

目录 1. XSS攻击 1.1 XSS攻击原理 1.2 XSS能做什么 1.3 XSS三种类型 1.4 XSS三种途径 1.5 XSS测试方法 1.5.1 查看代码 1.5.2 准备测试脚本 1.5.3 自动化测试XSS漏洞 1.5.4 XSS注入常用语句 1.6 XSS漏洞防范h3 1.6.1 对输入和URL参数进行过滤(白名单和黑名单) 1.6.…

tensorflow入门(一) 计算图、张量、会话的概念

1、计算图 计算图是tensorflow中最基本的一个概念,tensorflow中的所有计算都被转化成计算图上的节点。tensorflow的名字已经说明了它最重要的两个概念------tensor和flow。张量这个概念在数学或者物理学中可以有不同的解释,在tensorflow中,张…

基于tensorflow的深层神经网络(三)如何用tensorflow优化神经网络

1、神经网络优化算法 梯度下降算法主要用户优化单个参数的取值,而反向传播算法给出了一个高效的方式在所有参数上使用梯度下降算法,从而使神经网络模型在训练数据上的损失函数尽可能小。反向传播算法是训练神经网络的核心算法,它可以根据定义…