這是一個建立於 的文章,其中的資訊可能已經有所發展或是發生改變。
有限狀態機器(Finite-state machine, 簡寫FSM)又可以稱作有限狀態自動機。它必須是可以附著在某種事物上的,且該事物的狀態是有限的,通過某些觸發事件,會讓其狀態發生轉換。為此,有限狀態機器就是描述這些有限的狀態和觸發事件及轉換行為的數學模型。
有限狀態機器組成
有限狀態機器有兩個必要的特點,一是離散的,二是有限的。基於這兩點,現實世界上絕大多數事物因為複雜的狀態而無法用有限狀態機器表示。
而描述事物的有限狀態機器模型的元素由以下組成:
- 狀態(State):事物的狀態,包括初始狀態和所有事件觸發後的狀態
- 事件(Event):觸發狀態變化或者保持原狀態的事件
- 行為或轉換(Action/Transition):執行狀態轉換的過程
- 檢測器(Guard):檢測某種狀態要轉換成另一種狀態的條件是否滿足
應用領域
除了剛剛介紹的數學模型應用,有限狀態機器在許多不同領域都有重要應用,包括電氣工程、語言學、電腦科學、哲學、生物學、數學和邏輯學。有限狀態機器歸屬於自動機理論,下面的自動機理論的領域分層圖中就可以看出,越是外層的概念越複雜。
有限狀態機器的舉例
我們就拿身邊最經典的電風扇來舉例。假如電風扇有4個按鈕,分別是關、1檔、2檔和3檔,關按鈕負責關閉電風扇,也就是停止電風扇的轉動;而1、2、3檔都可以讓電風扇開啟,且風扇轉動的速度不一樣,產生的風力也不一樣。
這時我們判斷出風扇的4個狀態,分別是關閉(poweroff)、1檔(1st gear)、2檔(2nd gear)、3檔(3rd gear)。而4個按鈕的按下操作可以影響電風扇的狀態。下面用狀態圖來說明:
如果看不清楚,還有狀態跳躍表
為了更直觀的讓程式員瞭解FSM具體有什麼用,我將電風扇的有限狀態機器用程式來示範。
Go語言下的有限狀態機器
一共2個檔案,fsm.go是有限狀態機器的抽象定義,main.go裡是有限狀態機器在電風扇上的具體狀態呈現,代碼如下:
// fsm.gopackage mainimport ( "fmt" "sync")type FSMState string // 狀態type FSMEvent string // 事件type FSMHandler func() FSMState // 處理方法,並返回新的狀態// 有限狀態機器type FSM struct { mu sync.Mutex // 獨佔鎖定 state FSMState // 目前狀態 handlers map[FSMState]map[FSMEvent]FSMHandler // 處理地圖集,每一個狀態都可以出發有限個事件,執行有限個處理}// 擷取目前狀態func (f *FSM) getState() FSMState { return f.state}// 設定目前狀態func (f *FSM) setState(newState FSMState) { f.state = newState}// 某狀態添加事件處理方法func (f *FSM) AddHandler(state FSMState, event FSMEvent, handler FSMHandler) *FSM { if _, ok := f.handlers[state]; !ok { f.handlers[state] = make(map[FSMEvent]FSMHandler) } if _, ok := f.handlers[state][event]; ok { fmt.Printf("[警告] 狀態(%s)事件(%s)已定義過", state, event) } f.handlers[state][event] = handler return f}// 事件處理func (f *FSM) Call(event FSMEvent) FSMState { f.mu.Lock() defer f.mu.Unlock() events := f.handlers[f.getState()] if events == nil { return f.getState() } if fn, ok := events[event]; ok { oldState := f.getState() f.setState(fn()) newState := f.getState() fmt.Println("狀態從 [", oldState, "] 變成 [", newState, "]") } return f.getState()}// 執行個體化FSMfunc NewFSM(initState FSMState) *FSM { return &FSM{ state: initState, handlers: make(map[FSMState]map[FSMEvent]FSMHandler), }}
// main.gopackage mainimport ( "fmt")var ( Poweroff = FSMState("關閉") FirstGear = FSMState("1檔") SecondGear = FSMState("2檔") ThirdGear = FSMState("3檔") PowerOffEvent = FSMEvent("按下關閉按鈕") FirstGearEvent = FSMEvent("按下1檔按鈕") SecondGearEvent = FSMEvent("按下2檔按鈕") ThirdGearEvent = FSMEvent("按下3檔按鈕") PowerOffHandler = FSMHandler(func() FSMState { fmt.Println("電風扇已關閉") return Poweroff }) FirstGearHandler = FSMHandler(func() FSMState { fmt.Println("電風扇開啟1檔,微風徐來!") return FirstGear }) SecondGearHandler = FSMHandler(func() FSMState { fmt.Println("電風扇開啟2檔,涼颼颼!") return SecondGear }) ThirdGearHandler = FSMHandler(func() FSMState { fmt.Println("電風扇開啟3檔,髮型被吹亂了!") return ThirdGear }))// 電風扇type ElectricFan struct { *FSM}// 執行個體化電風扇func NewElectricFan(initState FSMState) *ElectricFan { return &ElectricFan{ FSM: NewFSM(initState), }}// 入口函數func main() { efan := NewElectricFan(Poweroff) // 初始狀態是關閉的 // 關閉狀態 efan.AddHandler(Poweroff, PowerOffEvent, PowerOffHandler) efan.AddHandler(Poweroff, FirstGearEvent, FirstGearHandler) efan.AddHandler(Poweroff, SecondGearEvent, SecondGearHandler) efan.AddHandler(Poweroff, ThirdGearEvent, ThirdGearHandler) // 1檔狀態 efan.AddHandler(FirstGear, PowerOffEvent, PowerOffHandler) efan.AddHandler(FirstGear, FirstGearEvent, FirstGearHandler) efan.AddHandler(FirstGear, SecondGearEvent, SecondGearHandler) efan.AddHandler(FirstGear, ThirdGearEvent, ThirdGearHandler) // 2檔狀態 efan.AddHandler(SecondGear, PowerOffEvent, PowerOffHandler) efan.AddHandler(SecondGear, FirstGearEvent, FirstGearHandler) efan.AddHandler(SecondGear, SecondGearEvent, SecondGearHandler) efan.AddHandler(SecondGear, ThirdGearEvent, ThirdGearHandler) // 3檔狀態 efan.AddHandler(ThirdGear, PowerOffEvent, PowerOffHandler) efan.AddHandler(ThirdGear, FirstGearEvent, FirstGearHandler) efan.AddHandler(ThirdGear, SecondGearEvent, SecondGearHandler) efan.AddHandler(ThirdGear, ThirdGearEvent, ThirdGearHandler) // 開始測試狀態變化 efan.Call(ThirdGearEvent) // 按下3檔按鈕 efan.Call(FirstGearEvent) // 按下1檔按鈕 efan.Call(PowerOffEvent) // 按下關閉按鈕 efan.Call(SecondGearEvent) // 按下2檔按鈕 efan.Call(PowerOffEvent) // 按下關閉按鈕}
執行後返回:
電風扇開啟3檔,髮型被吹亂了!狀態從 [ 關閉 ] 變成 [ 3檔 ]電風扇開啟1檔,微風徐來!狀態從 [ 3檔 ] 變成 [ 1檔 ]電風扇已關閉狀態從 [ 1檔 ] 變成 [ 關閉 ]電風扇開啟2檔,涼颼颼!狀態從 [ 關閉 ] 變成 [ 2檔 ]電風扇已關閉狀態從 [ 2檔 ] 變成 [ 關閉 ]