Go语言中的有限状态机FSM怎么使用
来源:亿速云
时间:2023-05-03 19:35:19 120浏览 收藏
知识点掌握了,还需要不断练习才能熟练运用。下面golang学习网给大家带来一个Golang开发实战,手把手教大家学习《Go语言中的有限状态机FSM怎么使用》,在实现功能的过程中也带大家重新温习相关知识点,温故而知新,回头看看说不定又有不一样的感悟!
本文小编为大家详细介绍“Go语言中的有限状态机FSM怎么使用”,内容详细,步骤清晰,细节处理妥当,希望这篇“Go语言中的有限状态机FSM怎么使用”文章能帮助大家解决疑惑,下面跟着小编的思路慢慢深入,一起来学习新知识吧。
1、FSM简介
1.1 有限状态机的定义
有限状态机(Finite State Machine,FSM)是一种数学模型,用于描述系统在不同状态下的行为和转移条件。
状态机有三个组成部分:状态(State)、事件(Event)、动作(Action)
,事件(转移条件)触发状态的转移和动作的执行。动作的执行不是必须的,可以只转移状态,不指定任何动作。总体而言,状态机是一种用以表示有限个状态以及这些状态之间的转移和动作的执行等行为的数学模型。
状态机可以用公式State(S) , Event(E) -> Actions (A), State(S’)
表示,即在处于状态S的情况下,接收到了事件E,使得状态转移到了S’,同时伴随着动作A的执行。
Event(事件)
是指触发状态转换的输入信号或条件。它可以是任何类型的输入,例如传感器数据、用户输入、网络消息等。在编程中,Event通常是一个枚举类型,每个枚举值代表一个特定的事件。
State(状态)
是指系统在某一时刻所处的状态,它是系统的一种抽象描述。在有限状态机中,状态是由一组状态变量来描述的,这些状态变量的取值决定了系统的状态。状态可以是离散的,也可以是连续的。在有限状态机中,状态通常用一个圆圈来表示,圆圈内部写上状态的名称。例如,一个简单的有限状态机可以有两个状态:开和关,它们可以用以下方式表示:
Action(动作)
是指在状态转移时执行的操作或动作。当有限状态机从一个状态转移到另一个状态时,可以执行一个或多个action来改变系统的状态或执行某些操作。例如,当有限状态机从“待机”状态转移到“运行”状态时,可以执行一个action来启动系统。在实际应用中,action可以是任何有效的代码,例如函数调用、变量赋值、打印输出等。
FSM 通常用于编程中,用于实现状态转移和控制流程。
注意:
在任何时刻,FSM 只能处于一种状态。
1.2 Go中的FSM
通过上面关于有限状态机的定义,我们大概知道了状态机是个什么东西,那么Golang中是怎么实现的呢。不用慌,已经有大佬实现好了,只管用就好了。
安装:
go get github.com/looplab/fsm@v1.0.1
接下来一起看看github.com/looplab/fsm
是如何使用的。
2、github.com/looplab/fsm 如何使用
注意:
不同版本的 fsm 使用方式,可能不太一样,最好是看下 NewFSM
函数的注释,看下具体的细节。 本篇文章以:github.com/looplab/fsm@v1.0.1
为例。
2.1 fsm 基础使用
这里把官方的例子改了下,感觉官方的例子不是很清晰。代码如下:
package main import ( "context" "fmt" "github.com/looplab/fsm" ) type Door struct { Name string FSM *fsm.FSM } func NewDoor(name string) *Door { d := &Door{ Name: name, } d.FSM = fsm.NewFSM( "closed", fsm.Events{ {Name: "open", Src: []string{"closed"}, Dst: "open"}, {Name: "close", Src: []string{"open"}, Dst: "closed"}, }, fsm.Callbacks{ "enter_state": func(_ context.Context, e *fsm.Event) { d.enterState(e) }, }, ) return d } func (d *Door) enterState(e *fsm.Event) { fmt.Printf("The door's name:%s , current state:%s\n", d.Name, e.Dst) } func main() { door := NewDoor("测试") fmt.Printf("fsm current state: %s \n", door.FSM.Current()) err := door.FSM.Event(context.Background(), "open") if err != nil { fmt.Println(err) } fmt.Printf("fsm current state: %s \n", door.FSM.Current()) err = door.FSM.Event(context.Background(), "close") if err != nil { fmt.Println(err) } fmt.Printf("fsm current state: %s \n", door.FSM.Current()) }
执行结果:
fsm current state: closed
The door's name:测试 , current state:open
fsm current state: open
The door's name:测试 , current state:closed
fsm current state: closed
这里就通过Event
改变FSM中的状态。转移公式为:Src,Event -> Dst,d.enterState
。大意就是接受到了输入Event
,状态机的State
由Src->Dst
,并且执行了Action
:d.enterState。
2.2 fsm 中 Action 何时执行
刚开始使用的时候,好奇d.enterState(e)
是什么时候调用的,我们一起看看 NewFSM
中的注释就清楚了。
// NewFSM constructs a FSM from events and callbacks. // // The events and transitions are specified as a slice of Event structs // specified as Events. Each Event is mapped to one or more internal // transitions from Event.Src to Event.Dst. // Callbacks are added as a map specified as Callbacks where the key is parsed // as the callback event as follows, and called in the same order: // // 1. before_- called before event named // // 2. before_event - called before all events // // 3. leave_ - called before leaving // // 4. leave_state - called before leaving all states // // 5. enter_ - called after entering // // 6. enter_state - called after entering all states // // 7. after_ - called after event named // // 8. after_event - called after all events // // There are also two short form versions for the most commonly used callbacks. // They are simply the name of the event or state: // // 1. - called after entering // // 2. - called after event named // // If both a shorthand version and a full version is specified it is undefined // which version of the callback will end up in the internal map. This is due // to the pseudo random nature of Go maps. No checking for multiple keys is // currently performed.
从上面我们知道了,d.enterState(e)
是在called after entering all states
时执行的。
2.2.1 完整版书写的Callbacks执行顺序
从上面的注释能知道完整版书写的Callbacks
的执行顺序如下:
2.2.2 简写版的Callbacks执行顺序
2.2.3 注意事项
虽然Callbacks
的写法有两种,但是不能同时使用完整版和简写版,否则最终使用那个版本是不确定的。
2.3 较为完整的例子
package main import ( "context" "fmt" "github.com/looplab/fsm" ) type Door struct { Name string FSM *fsm.FSM } func NewDoor(name string) *Door { d := &Door{ Name: name, } d.FSM = fsm.NewFSM( "closed", fsm.Events{ {Name: "open", Src: []string{"closed"}, Dst: "open"}, {Name: "close", Src: []string{"open"}, Dst: "closed"}, }, fsm.Callbacks{ "before_open": func(_ context.Context, e *fsm.Event) { d.beforeOpen(e) }, "before_event": func(_ context.Context, e *fsm.Event) { d.beforeEvent(e) }, "leave_closed": func(_ context.Context, e *fsm.Event) { d.leaveClosed(e) }, "leave_state": func(_ context.Context, e *fsm.Event) { d.leaveState(e) }, "enter_open": func(_ context.Context, e *fsm.Event) { d.enterOpen(e) }, "enter_state": func(_ context.Context, e *fsm.Event) { d.enterState(e) }, "after_open": func(_ context.Context, e *fsm.Event) { d.afterOpen(e) }, "after_event": func(_ context.Context, e *fsm.Event) { d.afterEvent(e) }, }, ) return d } func (d *Door) beforeOpen(e *fsm.Event) { fmt.Printf("beforeOpen, current state:%s, Dst:%s \n", d.FSM.Current(), e.Dst) } func (d *Door) beforeEvent(e *fsm.Event) { fmt.Printf("beforeEvent, current state:%s, Dst:%s \n", d.FSM.Current(), e.Dst) } func (d *Door) leaveClosed(e *fsm.Event) { fmt.Printf("leaveClosed, current state:%s, Dst:%s \n", d.FSM.Current(), e.Dst) } func (d *Door) leaveState(e *fsm.Event) { fmt.Printf("leaveState, current state:%s, Dst:%s \n", d.FSM.Current(), e.Dst) } func (d *Door) enterOpen(e *fsm.Event) { fmt.Printf("enterOpen, current state:%s, Dst:%s \n", d.FSM.Current(), e.Dst) } func (d *Door) enterState(e *fsm.Event) { fmt.Printf("enterState, current state:%s, Dst:%s \n", d.FSM.Current(), e.Dst) } func (d *Door) afterOpen(e *fsm.Event) { fmt.Printf("afterOpen, current state:%s, Dst:%s \n", d.FSM.Current(), e.Dst) } func (d *Door) afterEvent(e *fsm.Event) { fmt.Printf("afterEvent, current state:%s, Dst:%s \n", d.FSM.Current(), e.Dst) } func main() { door := NewDoor("测试") fmt.Printf("fsm current state: %s \n", door.FSM.Current()) err := door.FSM.Event(context.Background(), "open") if err != nil { fmt.Println(err) } fmt.Printf("fsm current state: %s \n", door.FSM.Current()) err = door.FSM.Event(context.Background(), "close") if err != nil { fmt.Println(err) } fmt.Printf("fsm current state: %s \n", door.FSM.Current()) }
执行结果:大家重点看current state
何时发生的变化。
fsm current state: closed
beforeOpen, current state:closed, Dst:open
beforeEvent, current state:closed, Dst:open
leaveClosed, current state:closed, Dst:open
leaveState, current state:closed, Dst:open
enterOpen, current state:open, Dst:open
enterState, current state:open, Dst:open
afterOpen, current state:open, Dst:open
afterEvent, current state:open, Dst:open
fsm current state: open
beforeEvent, current state:open, Dst:closed
leaveState, current state:open, Dst:closed
enterState, current state:closed, Dst:closed
afterEvent, current state:closed, Dst:closed
fsm current state: closed
读到这里,这篇“Go语言中的有限状态机FSM怎么使用”文章已经介绍完毕,想要掌握这篇文章的知识点还需要大家自己动手实践使用过才能领会,如果想了解更多相关内容的文章,欢迎关注golang学习网行业资讯频道。
今天关于《Go语言中的有限状态机FSM怎么使用》的内容介绍就到此结束,如果有什么疑问或者建议,可以在golang学习网公众号下多多回复交流;文中若有不正之处,也希望回复留言以告知!
-
185 收藏
-
460 收藏
-
430 收藏
-
450 收藏
-
320 收藏
-
233 收藏
-
322 收藏
-
181 收藏
-
316 收藏
-
244 收藏
-
300 收藏
-
- 前端进阶之JavaScript设计模式
- 设计模式是开发人员在软件开发过程中面临一般问题时的解决方案,代表了最佳的实践。本课程的主打内容包括JS常见设计模式以及具体应用场景,打造一站式知识长龙服务,适合有JS基础的同学学习。
- 立即学习 542次学习
-
- GO语言核心编程课程
- 本课程采用真实案例,全面具体可落地,从理论到实践,一步一步将GO核心编程技术、编程思想、底层实现融会贯通,使学习者贴近时代脉搏,做IT互联网时代的弄潮儿。
- 立即学习 507次学习
-
- 简单聊聊mysql8与网络通信
- 如有问题加微信:Le-studyg;在课程中,我们将首先介绍MySQL8的新特性,包括性能优化、安全增强、新数据类型等,帮助学生快速熟悉MySQL8的最新功能。接着,我们将深入解析MySQL的网络通信机制,包括协议、连接管理、数据传输等,让
- 立即学习 497次学习
-
- JavaScript正则表达式基础与实战
- 在任何一门编程语言中,正则表达式,都是一项重要的知识,它提供了高效的字符串匹配与捕获机制,可以极大的简化程序设计。
- 立即学习 487次学习
-
- 从零制作响应式网站—Grid布局
- 本系列教程将展示从零制作一个假想的网络科技公司官网,分为导航,轮播,关于我们,成功案例,服务流程,团队介绍,数据部分,公司动态,底部信息等内容区块。网站整体采用CSSGrid布局,支持响应式,有流畅过渡和展现动画。
- 立即学习 484次学习