您好,登錄后才能下訂單哦!
本文小編為大家詳細介紹“Go語言中的有限狀態(tài)機FSM怎么使用”,內(nèi)容詳細,步驟清晰,細節(jié)處理妥當(dāng),希望這篇“Go語言中的有限狀態(tài)機FSM怎么使用”文章能幫助大家解決疑惑,下面跟著小編的思路慢慢深入,一起來學(xué)習(xí)新知識吧。
有限狀態(tài)機(Finite State Machine,F(xiàn)SM)是一種數(shù)學(xué)模型,用于描述系統(tǒng)在不同狀態(tài)下的行為和轉(zhuǎn)移條件。
狀態(tài)機有三個組成部分:狀態(tài)(State)、事件(Event)、動作(Action)
,事件(轉(zhuǎn)移條件)觸發(fā)狀態(tài)的轉(zhuǎn)移和動作的執(zhí)行。動作的執(zhí)行不是必須的,可以只轉(zhuǎn)移狀態(tài),不指定任何動作??傮w而言,狀態(tài)機是一種用以表示有限個狀態(tài)以及這些狀態(tài)之間的轉(zhuǎn)移和動作的執(zhí)行等行為的數(shù)學(xué)模型。
狀態(tài)機可以用公式State(S) , Event(E) -> Actions (A), State(S’)
表示,即在處于狀態(tài)S的情況下,接收到了事件E,使得狀態(tài)轉(zhuǎn)移到了S’,同時伴隨著動作A的執(zhí)行。
Event(事件)
是指觸發(fā)狀態(tài)轉(zhuǎn)換的輸入信號或條件。它可以是任何類型的輸入,例如傳感器數(shù)據(jù)、用戶輸入、網(wǎng)絡(luò)消息等。在編程中,Event通常是一個枚舉類型,每個枚舉值代表一個特定的事件。
State(狀態(tài))
是指系統(tǒng)在某一時刻所處的狀態(tài),它是系統(tǒng)的一種抽象描述。在有限狀態(tài)機中,狀態(tài)是由一組狀態(tài)變量來描述的,這些狀態(tài)變量的取值決定了系統(tǒng)的狀態(tài)。狀態(tài)可以是離散的,也可以是連續(xù)的。在有限狀態(tài)機中,狀態(tài)通常用一個圓圈來表示,圓圈內(nèi)部寫上狀態(tài)的名稱。例如,一個簡單的有限狀態(tài)機可以有兩個狀態(tài):開和關(guān),它們可以用以下方式表示:
Action(動作)
是指在狀態(tài)轉(zhuǎn)移時執(zhí)行的操作或動作。當(dāng)有限狀態(tài)機從一個狀態(tài)轉(zhuǎn)移到另一個狀態(tài)時,可以執(zhí)行一個或多個action來改變系統(tǒng)的狀態(tài)或執(zhí)行某些操作。例如,當(dāng)有限狀態(tài)機從“待機”狀態(tài)轉(zhuǎn)移到“運行”狀態(tài)時,可以執(zhí)行一個action來啟動系統(tǒng)。在實際應(yīng)用中,action可以是任何有效的代碼,例如函數(shù)調(diào)用、變量賦值、打印輸出等。
FSM 通常用于編程中,用于實現(xiàn)狀態(tài)轉(zhuǎn)移和控制流程。
注意:
在任何時刻,F(xiàn)SM 只能處于一種狀態(tài)。
通過上面關(guān)于有限狀態(tài)機的定義,我們大概知道了狀態(tài)機是個什么東西,那么Golang中是怎么實現(xiàn)的呢。不用慌,已經(jīng)有大佬實現(xiàn)好了,只管用就好了。
安裝:
go get github.com/looplab/fsm@v1.0.1
接下來一起看看github.com/looplab/fsm
是如何使用的。
注意:
不同版本的 fsm 使用方式,可能不太一樣,最好是看下 NewFSM
函數(shù)的注釋,看下具體的細節(jié)。 本篇文章以:github.com/looplab/fsm@v1.0.1
為例。
這里把官方的例子改了下,感覺官方的例子不是很清晰。代碼如下:
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()) }
執(zhí)行結(jié)果:
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中的狀態(tài)。轉(zhuǎn)移公式為:Src,Event -> Dst,d.enterState
。大意就是接受到了輸入Event
,狀態(tài)機的State
由Src->Dst
,并且執(zhí)行了Action
:d.enterState。
剛開始使用的時候,好奇d.enterState(e)
是什么時候調(diào)用的,我們一起看看 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_<EVENT> - called before event named <EVENT> // // 2. before_event - called before all events // // 3. leave_<OLD_STATE> - called before leaving <OLD_STATE> // // 4. leave_state - called before leaving all states // // 5. enter_<NEW_STATE> - called after entering <NEW_STATE> // // 6. enter_state - called after entering all states // // 7. after_<EVENT> - called after event named <EVENT> // // 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. <NEW_STATE> - called after entering <NEW_STATE> // // 2. <EVENT> - called after event named <EVENT> // // 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
時執(zhí)行的。
2.2.1 完整版書寫的Callbacks執(zhí)行順序
從上面的注釋能知道完整版書寫的Callbacks
的執(zhí)行順序如下:
2.2.2 簡寫版的Callbacks執(zhí)行順序
2.2.3 注意事項
雖然Callbacks
的寫法有兩種,但是不能同時使用完整版和簡寫版,否則最終使用那個版本是不確定的。
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()) }
執(zhí)行結(jié)果:大家重點看current state
何時發(fā)生的變化。
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語言中的有限狀態(tài)機FSM怎么使用”文章已經(jīng)介紹完畢,想要掌握這篇文章的知識點還需要大家自己動手實踐使用過才能領(lǐng)會,如果想了解更多相關(guān)內(nèi)容的文章,歡迎關(guān)注億速云行業(yè)資訊頻道。
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關(guān)證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。