有限狀態(tài)機FSM的原理與GO的實現(xiàn)

有限狀態(tài)機(Finite-state machine, 簡寫FSM)又可以稱作有限狀態(tài)自動機。它必須是可以附著在某種事物上的革屠,且該事物的狀態(tài)是有限的,通過某些觸發(fā)事件,會讓其狀態(tài)發(fā)生轉換扁远。為此,有限狀態(tài)機就是描述這些有限的狀態(tài)和觸發(fā)事件及轉換行為的數(shù)學模型刻像。

有限狀態(tài)機組成

有限狀態(tài)機有兩個必要的特點畅买,一是離散的,二是有限的绎速∑せ瘢基于這兩點,現(xiàn)實世界上絕大多數(shù)事物因為復雜的狀態(tài)而無法用有限狀態(tài)機表示纹冤。

而描述事物的有限狀態(tài)機模型的元素由以下組成:

  • 狀態(tài)(State):事物的狀態(tài)洒宝,包括初始狀態(tài)和所有事件觸發(fā)后的狀態(tài)
  • 事件(Event):觸發(fā)狀態(tài)變化或者保持原狀態(tài)的事件
  • 行為或轉換(Action/Transition):執(zhí)行狀態(tài)轉換的過程
  • 檢測器(Guard):檢測某種狀態(tài)要轉換成另一種狀態(tài)的條件是否滿足

應用領域

除了剛剛介紹的數(shù)學模型應用,有限狀態(tài)機在許多不同領域都有重要應用萌京,包括電氣工程雁歌、語言學、計算機科學知残、哲學靠瞎、生物學、數(shù)學和邏輯學。有限狀態(tài)機歸屬于自動機理論乏盐,下面的自動機理論的領域分層圖中就可以看出佳窑,越是外層的概念越復雜。

有限狀態(tài)機的舉例

我們就拿身邊最經典的電風扇來舉例父能。假如電風扇有4個按鈕神凑,分別是關、1檔何吝、2檔和3檔溉委,關按鈕負責關閉電風扇,也就是停止電風扇的轉動爱榕;而1瓣喊、2、3檔都可以讓電風扇開啟黔酥,且風扇轉動的速度不一樣藻三,產生的風力也不一樣。

這時我們判斷出風扇的4個狀態(tài)絮爷,分別是關閉(poweroff)趴酣、1檔(1st gear)、2檔(2nd gear)坑夯、3檔(3rd gear)岖寞。而4個按鈕的按下操作可以影響電風扇的狀態(tài)。下面用狀態(tài)圖來說明:

如果看不清楚柜蜈,還有狀態(tài)轉移表

為了更直觀的讓程序員了解FSM具體有什么用仗谆,我將電風扇的有限狀態(tài)機用程序來演示。

Go語言下的有限狀態(tài)機

一共2個文件淑履,fsm.go是有限狀態(tài)機的抽象定義隶垮,main.go里是有限狀態(tài)機在電風扇上的具體狀態(tài)呈現(xiàn),代碼如下:

// fsm.go
package main

import (
    "fmt"
    "sync"
)

type FSMState string            // 狀態(tài)
type FSMEvent string            // 事件
type FSMHandler func() FSMState // 處理方法秘噪,并返回新的狀態(tài)

// 有限狀態(tài)機
type FSM struct {
    mu       sync.Mutex                           // 排他鎖
    state    FSMState                             // 當前狀態(tài)
    handlers map[FSMState]map[FSMEvent]FSMHandler // 處理地圖集狸吞,每一個狀態(tài)都可以出發(fā)有限個事件,執(zhí)行有限個處理
}

// 獲取當前狀態(tài)
func (f *FSM) getState() FSMState {
    return f.state
}

// 設置當前狀態(tài)
func (f *FSM) setState(newState FSMState) {
    f.state = newState
}

// 某狀態(tài)添加事件處理方法
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("[警告] 狀態(tài)(%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("狀態(tài)從 [", oldState, "] 變成 [", newState, "]")
    }
    return f.getState()
}

// 實例化FSM
func NewFSM(initState FSMState) *FSM {
    return &FSM{
        state:    initState,
        handlers: make(map[FSMState]map[FSMEvent]FSMHandler),
    }
}

// main.go
package main

import (
    "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檔威始,發(fā)型被吹亂了!")
        return ThirdGear
    })
)

// 電風扇
type ElectricFan struct {
    *FSM
}

// 實例化電風扇
func NewElectricFan(initState FSMState) *ElectricFan {
    return &ElectricFan{
        FSM: NewFSM(initState),
    }
}

// 入口函數(shù)
func main() {

    efan := NewElectricFan(Poweroff) // 初始狀態(tài)是關閉的
    // 關閉狀態(tài)
    efan.AddHandler(Poweroff, PowerOffEvent, PowerOffHandler)
    efan.AddHandler(Poweroff, FirstGearEvent, FirstGearHandler)
    efan.AddHandler(Poweroff, SecondGearEvent, SecondGearHandler)
    efan.AddHandler(Poweroff, ThirdGearEvent, ThirdGearHandler)
    // 1檔狀態(tài)
    efan.AddHandler(FirstGear, PowerOffEvent, PowerOffHandler)
    efan.AddHandler(FirstGear, FirstGearEvent, FirstGearHandler)
    efan.AddHandler(FirstGear, SecondGearEvent, SecondGearHandler)
    efan.AddHandler(FirstGear, ThirdGearEvent, ThirdGearHandler)
    // 2檔狀態(tài)
    efan.AddHandler(SecondGear, PowerOffEvent, PowerOffHandler)
    efan.AddHandler(SecondGear, FirstGearEvent, FirstGearHandler)
    efan.AddHandler(SecondGear, SecondGearEvent, SecondGearHandler)
    efan.AddHandler(SecondGear, ThirdGearEvent, ThirdGearHandler)
    // 3檔狀態(tài)
    efan.AddHandler(ThirdGear, PowerOffEvent, PowerOffHandler)
    efan.AddHandler(ThirdGear, FirstGearEvent, FirstGearHandler)
    efan.AddHandler(ThirdGear, SecondGearEvent, SecondGearHandler)
    efan.AddHandler(ThirdGear, ThirdGearEvent, ThirdGearHandler)

    // 開始測試狀態(tài)變化
    efan.Call(ThirdGearEvent)  // 按下3檔按鈕
    efan.Call(FirstGearEvent)  // 按下1檔按鈕
    efan.Call(PowerOffEvent)   // 按下關閉按鈕
    efan.Call(SecondGearEvent) // 按下2檔按鈕
    efan.Call(PowerOffEvent)   // 按下關閉按鈕
}

執(zhí)行后返回:

電風扇開啟3檔像街,發(fā)型被吹亂了黎棠!
狀態(tài)從 [ 關閉 ] 變成 [ 3檔 ]
電風扇開啟1檔晋渺,微風徐來!
狀態(tài)從 [ 3檔 ] 變成 [ 1檔 ]
電風扇已關閉
狀態(tài)從 [ 1檔 ] 變成 [ 關閉 ]
電風扇開啟2檔脓斩,涼颼颼木西!
狀態(tài)從 [ 關閉 ] 變成 [ 2檔 ]
電風扇已關閉
狀態(tài)從 [ 2檔 ] 變成 [ 關閉 ]
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市俭厚,隨后出現(xiàn)的幾起案子户魏,更是在濱河造成了極大的恐慌,老刑警劉巖挪挤,帶你破解...
    沈念sama閱讀 216,591評論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異关翎,居然都是意外死亡扛门,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,448評論 3 392
  • 文/潘曉璐 我一進店門纵寝,熙熙樓的掌柜王于貴愁眉苦臉地迎上來论寨,“玉大人,你說我怎么就攤上這事爽茴≡岬剩” “怎么了?”我有些...
    開封第一講書人閱讀 162,823評論 0 353
  • 文/不壞的土叔 我叫張陵室奏,是天一觀的道長火焰。 經常有香客問我,道長胧沫,這世上最難降的妖魔是什么昌简? 我笑而不...
    開封第一講書人閱讀 58,204評論 1 292
  • 正文 為了忘掉前任,我火速辦了婚禮绒怨,結果婚禮上纯赎,老公的妹妹穿的比我還像新娘。我一直安慰自己南蹂,他們只是感情好犬金,可當我...
    茶點故事閱讀 67,228評論 6 388
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著六剥,像睡著了一般晚顷。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上仗考,一...
    開封第一講書人閱讀 51,190評論 1 299
  • 那天音同,我揣著相機與錄音,去河邊找鬼秃嗜。 笑死权均,一個胖子當著我的面吹牛顿膨,可吹牛的內容都是我干的。 我是一名探鬼主播叽赊,決...
    沈念sama閱讀 40,078評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼恋沃,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了必指?” 一聲冷哼從身側響起囊咏,我...
    開封第一講書人閱讀 38,923評論 0 274
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎塔橡,沒想到半個月后梅割,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經...
    沈念sama閱讀 45,334評論 1 310
  • 正文 獨居荒郊野嶺守林人離奇死亡葛家,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 37,550評論 2 333
  • 正文 我和宋清朗相戀三年户辞,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片癞谒。...
    茶點故事閱讀 39,727評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡底燎,死狀恐怖,靈堂內的尸體忽然破棺而出弹砚,到底是詐尸還是另有隱情双仍,我是刑警寧澤,帶...
    沈念sama閱讀 35,428評論 5 343
  • 正文 年R本政府宣布桌吃,位于F島的核電站朱沃,受9級特大地震影響,放射性物質發(fā)生泄漏读存。R本人自食惡果不足惜为流,卻給世界環(huán)境...
    茶點故事閱讀 41,022評論 3 326
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望让簿。 院中可真熱鬧敬察,春花似錦、人聲如沸尔当。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,672評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽椭迎。三九已至锐帜,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間畜号,已是汗流浹背缴阎。 一陣腳步聲響...
    開封第一講書人閱讀 32,826評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留简软,地道東北人蛮拔。 一個月前我還...
    沈念sama閱讀 47,734評論 2 368
  • 正文 我出身青樓述暂,卻偏偏與公主長得像,于是被迫代替她去往敵國和親建炫。 傳聞我的和親對象是個殘疾皇子畦韭,可洞房花燭夜當晚...
    茶點故事閱讀 44,619評論 2 354

推薦閱讀更多精彩內容