使用coroutine實現狀態機(2)
自從 使用coroutine實現狀態機 以來已經有兩個星期了,在這兩個星期里我把這個想發給實現了。語法做了一點點小修改,但是總的來說沒有區別。上一篇文章的計算器的例子作為單元測試的一部分被添加了進去,大家可以在看這兩個文件:
CoSmcCalculator.txt:測試用的狀態機,支持加法和乘法。
CoSmcCalculator.cpp:生成的C++代碼。C++當然沒有狀態機的功能,所以Workflow腳本的狀態機自然被改寫成了普通的代碼,變成了一個真正的狀態機。
Workflow腳本要求使用了$state_machine的類必須繼承自system::StateMachine,再加上Workflow不允許多重繼承裡面出現同一個類多次,所以$state_machine的父類當然不能是$state_machine。這個限制是草稿裡面沒有的。
整個狀態機被編譯成了一個使用 coroutine 表達的狀態機,而coroutine又被編譯成了一個狀態機,所以生成的代碼裡面兩個嵌套的狀態機被合併到了一起,這麼掃一眼根本看不懂(逃
Workflow的狀態機支持$goto_state和$push_state兩種模式。$goto_state說的是你跳轉過去之後就不回來了,如果那個狀態結束了之後沒有跳轉到任何其他狀態,那麼整個狀態機就結束了。$push_state就是還要回來的疑似。$goto_state就像jump,$push_state就像函數調用。函數調用自然是有堆棧的,所以$push_state自然也有堆棧——這個被生成的coroutine的previousCoroutine變數隱式表達成了一個外部訪問不了的鏈表。
這個鏈表的根節點放在了system::StateMachine類裡面,類的 ResumeStateMachine 函數識別了多個coroutine之間切換的意圖,把他們有機地結合了起來。因為一個coroutine的暫停和結束,有可能意味著$push_state和"pop_state",自然不能讓ResumeStateMachine暫停,而是要繼續跑下去,直到狀態機真的要開始等待外部的輸入為止。
狀態機的輸入是用$state_input定義的,最後他們就變成了函數。你調用這些函數,就是在輸入數據。因此最後在測試用例裡面,我們的用例長這樣子:
func main():stringn{ntvar c = new SMCalculator^();nts=$"[$(c.Value)]";ntvar handler = attach(c.ValueChanged, func():voidnt{ntts=$"$(s)[$(c.Value)]";nt});nntc.Digit(1);t// 1ntc.Dot();t// 1.ntc.Digit(5);t// 1.5ntc.Add();ntc.Digit(2);t// 2ntc.Digit(1);t// 21ntc.Dot();t// 21.ntc.Digit(2);t// 21.2ntc.Digit(5);t// 21.25ntc.Mul();t// 22.75 (1.5 + 21.25)ntc.Digit(2);t// 2ntc.Equal();t// 45.5 (22.75 * 2)ntc.Clear();t// 0nntdetach(c.ValueChanged, handler);ntreturn s;n}n
這個代碼模擬了用戶點擊計算器按鈕的過程,Value屬性代表了液晶屏上顯示的數據,每次有改變的時候,這個值就會被append到全局變數s裡面。最後輸出的結果自然應該是:
[0][1][1.][1.5][2][21][21.][21.2][21.25][22.75][2][45.5][0]n
因為你在點計算器的時候,上面的數字的確就是這麼變化的。
推薦閱讀:
※Kotlin雜談(五) - Coroutines(三): 基本語法
※Unity 協程運行時的監控和優化
※使用coroutine實現狀態機