• 用go基于有向图实现一个有限状态机器(FSM)


    • 构建一个有向图:状态就是节点,状态到状态的事件就是边的类型
    • 一个FSM = FSM的当前状态 + 一个图
    • 事件发生,状态变更:以当前状态为from节点,事件的out边找到的那个顶点就是事件发生后的状态,然后把这个状态作为当前状态
    • 如下图就可以看作成一个FSM(一个图+当前状态):
      • 1、节点(状态)有:Locked、UnLocked
      • 2、边的类型(事件)有:Push、Coin;具体有4条边
      • 3、初始状态是Locked
      • 4、向初始的FSM发送事件Coin,以Locked为from定点,Coin为out边找到的节点就是UnLoked,然后把UnLoked作为当前状态

       

    • 代码:
      1. package main
      2. import (
      3. "fmt"
      4. "gonum.org/v1/gonum/graph"
      5. "gonum.org/v1/gonum/graph/multi"
      6. )
      7. /*
      8. 基于gonum定义节点和边
      9. */
      10. type State struct {
      11. Id int64
      12. Value interface{}
      13. }
      14. type Link struct {
      15. Id int64
      16. T, F graph.Node
      17. Event Event
      18. }
      19. func (n State) ID() int64 {
      20. return n.Id
      21. }
      22. func (n State) String() string {
      23. return n.Value.(string)
      24. }
      25. func (l Link) From() graph.Node {
      26. return l.F
      27. }
      28. func (l Link) To() graph.Node {
      29. return l.T
      30. }
      31. func (l Link) ID() int64 {
      32. return l.Id
      33. }
      34. func (l Link) ReversedLine() graph.Line {
      35. return Link{F: l.T, T: l.F}
      36. }
      37. /*
      38. 定义FSM
      39. */
      40. type Event string
      41. var NodeIDCnt = 0
      42. var LineIdCnt = 1
      43. type StateMachine struct {
      44. PresentState State
      45. g *multi.DirectedGraph
      46. }
      47. func New() *StateMachine {
      48. s := &StateMachine{}
      49. s.g = multi.NewDirectedGraph()
      50. return s
      51. }
      52. func (s *StateMachine) InitState(initValue interface{}) State {
      53. s.PresentState = s.NewState(initValue)
      54. return s.PresentState
      55. }
      56. func (s *StateMachine) NewState(stateValue interface{}) State {
      57. state := State{Id: int64(NodeIDCnt), Value: stateValue}
      58. s.g.AddNode(state)
      59. NodeIDCnt++
      60. return state
      61. }
      62. func (s *StateMachine) LinkStates(s1, s2 State, event Event) {
      63. s.g.SetLine(Link{F: s1, T: s2, Id: int64(LineIdCnt), Event: event})
      64. LineIdCnt++
      65. }
      66. // FireEvent 触发事件
      67. func (s *StateMachine) FireEvent(e Event) error {
      68. presentNode := s.PresentState
      69. it := s.g.From(presentNode.Id)
      70. for it.Next() {
      71. to := s.g.Node(it.Node().ID()).(State)
      72. line := graph.LinesOf(s.g.Lines(presentNode.Id, to.Id))[0].(Link)
      73. if line.Event == e {
      74. s.PresentState = to
      75. return nil
      76. }
      77. return fmt.Errorf("没有对应的事件:%s", e)
      78. }
      79. return nil
      80. }
      81. // Compute 批量触发处理
      82. func (s *StateMachine) Compute(events []string, printState bool) State {
      83. for _, e := range events {
      84. previousState := s.PresentState
      85. err := s.FireEvent(Event(e))
      86. if err != nil {
      87. panic(err)
      88. }
      89. if printState {
      90. fmt.Printf("触发事件[%s]后,状态由[%s]变为了[%s]\n", e, previousState, s.PresentState.String())
      91. }
      92. }
      93. return s.PresentState
      94. }
      95. func main() {
      96. //构造有向图
      97. stateMachine := New()
      98. lockedState := stateMachine.InitState("locked")
      99. unlockedSate := stateMachine.NewState("unlocked")
      100. coinEvent := Event("coin")
      101. pushEvent := Event("push")
      102. stateMachine.LinkStates(lockedState, unlockedSate, coinEvent)
      103. stateMachine.LinkStates(unlockedSate, lockedState, pushEvent)
      104. stateMachine.LinkStates(lockedState, lockedState, pushEvent)
      105. stateMachine.LinkStates(unlockedSate, unlockedSate, coinEvent)
      106. //触发事件
      107. fmt.Printf("初始状态: %s\n", stateMachine.PresentState.String())
      108. events := []string{"coin", "push"}
      109. stateMachine.Compute(events, true)
      110. fmt.Printf("最终状态: %s\n", stateMachine.PresentState.String())
      111. }
    • 输出结果:

    初始状态: locked
    触发事件[coin]后,状态由[locked]变为了[unlocked]
    触发事件[push]后,状态由[unlocked]变为了[locked]
    最终状态: locked

  • 相关阅读:
    linux docker安装SqlServer2019
    大数据平台数据脱敏是什么意思?有哪些方案?
    rmq nameserver
    [已解决]react打包部署
    JVM学习-监控工具(一)
    面试官:你知道 Java 中的回调机制吗?
    python——列表(数组)
    springboot整合mybatis、swagger、代码生成器、Lombok
    asp.net core EF Sqlserver
    【计算机图形学入门】笔记2:向量与线性代数(图形学中用到的线性代数)
  • 原文地址:https://blog.csdn.net/laughing_yang/article/details/125417945