用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作为当前状态

     

  • 代码:
    package main
    
    import (
    	"fmt"
    	"gonum.org/v1/gonum/graph"
    	"gonum.org/v1/gonum/graph/multi"
    )
    
    /*
    基于gonum定义节点和边
    */
    
    type State struct {
    	Id    int64
    	Value interface{}
    }
    
    type Link struct {
    	Id    int64
    	T, F  graph.Node
    	Event Event
    }
    
    func (n State) ID() int64 {
    	return n.Id
    }
    
    func (n State) String() string {
    	return n.Value.(string)
    }
    
    func (l Link) From() graph.Node {
    	return l.F
    }
    
    func (l Link) To() graph.Node {
    	return l.T
    }
    
    func (l Link) ID() int64 {
    	return l.Id
    }
    
    func (l Link) ReversedLine() graph.Line {
    	return Link{F: l.T, T: l.F}
    }
    
    /*
     定义FSM
    */
    
    type Event string
    
    var NodeIDCnt = 0
    var LineIdCnt = 1
    
    type StateMachine struct {
    	PresentState State
    	g            *multi.DirectedGraph
    }
    
    func New() *StateMachine {
    	s := &StateMachine{}
    	s.g = multi.NewDirectedGraph()
    	return s
    }
    
    func (s *StateMachine) InitState(initValue interface{}) State {
    	s.PresentState = s.NewState(initValue)
    	return s.PresentState
    }
    
    func (s *StateMachine) NewState(stateValue interface{}) State {
    	state := State{Id: int64(NodeIDCnt), Value: stateValue}
    	s.g.AddNode(state)
    	NodeIDCnt++
    	return state
    }
    
    func (s *StateMachine) LinkStates(s1, s2 State, event Event) {
    	s.g.SetLine(Link{F: s1, T: s2, Id: int64(LineIdCnt), Event: event})
    	LineIdCnt++
    }
    
    // FireEvent 触发事件
    func (s *StateMachine) FireEvent(e Event) error {
    	presentNode := s.PresentState
    
    	it := s.g.From(presentNode.Id)
    
    	for it.Next() {
    		to := s.g.Node(it.Node().ID()).(State)
    		line := graph.LinesOf(s.g.Lines(presentNode.Id, to.Id))[0].(Link)
    		if line.Event == e {
    			s.PresentState = to
    			return nil
    		}
    		return fmt.Errorf("没有对应的事件:%s", e)
    	}
    	return nil
    }
    
    // Compute 批量触发处理
    func (s *StateMachine) Compute(events []string, printState bool) State {
    	for _, e := range events {
    		previousState := s.PresentState
    		err := s.FireEvent(Event(e))
    		if err != nil {
    			panic(err)
    		}
    		if printState {
    			fmt.Printf("触发事件[%s]后,状态由[%s]变为了[%s]\n", e, previousState, s.PresentState.String())
    		}
    	}
    	return s.PresentState
    }
    
    func main() {
    	//构造有向图
    	stateMachine := New()
    	lockedState := stateMachine.InitState("locked")
    	unlockedSate := stateMachine.NewState("unlocked")
    
    	coinEvent := Event("coin")
    	pushEvent := Event("push")
    
    	stateMachine.LinkStates(lockedState, unlockedSate, coinEvent)
    	stateMachine.LinkStates(unlockedSate, lockedState, pushEvent)
    
    	stateMachine.LinkStates(lockedState, lockedState, pushEvent)
    	stateMachine.LinkStates(unlockedSate, unlockedSate, coinEvent)
    
    	//触发事件
    	fmt.Printf("初始状态: %s\n", stateMachine.PresentState.String())
    
    	events := []string{"coin", "push"}
    	stateMachine.Compute(events, true)
    
    	fmt.Printf("最终状态: %s\n", stateMachine.PresentState.String())
    
    }
  • 输出结果:

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

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值