函數式編程如何模擬有限狀態機?
比如用haskell
根據自動機的理論模型來就行, 用wikipedia里那個例子為例:
符號表:
&> symbols = [a..z]
狀態集:
&> data State = Start | NFound | IFound | CFound | Error | Success deriving Show
初始狀態集:
Start
終結狀態集:
[Error, Success]
映射函數:
&> match :: State -&> Char -&> State
&> match Start n = NFound
&> match Start _ = Error
&> match NFound i = IFound
&> match NFound _ = Error
&> match IFound c = CFound
&> match IFound _ = Error
&> match CFound e = Success
&> match CFound _ = Error
&> matchNice :: String -&> State
&> matchNice str = matchNice str Start
&> where
&> matchNice _ Error = Error
&> matchNice [] Success = Success
&> matchNice (x:xs) state = matchNice xs (match state x)
&> matchNice _ Success = Error
效率什麼的忽略, 大概是這個意思~
--補上一個用Elixir的宏的例子defmodule Fsm do
fsm = [
running: {:pause, :paused},
running: {:stop, :stopped},
paused: {:resume, :running}
]
for {state, {action, next_state}} &<- fsm do
def unquote(action)(unquote(state)), do: unquote(next_state)
end
def initial, do: :running
end
Fsm.initial
# :running
Fsm.initial |&> Fsm.pause
# :paused
Fsm.initial |&> Fsm.pause |&> Fsm.pause
# ** (FunctionClauseError) no function clause matching in Fsm.pause/1
via theerlangelist.com
你可以去看下CLaSH。用Haskell寫的代碼在FPGA上跑。
狀態機用mealy 和moore 這兩個組合子實現,或者用state monad實現。比如說,你要用mealy模型實現一個狀態機,你需要提供一個s -&> i -&> (s,o)的函數。當然是面向異常編程,多種範式結合的最佳實踐(
exception State_transfer of (char -&> string)
exception Terminating_state of string
let success = "Success"
let error = "Error"
let c_found = function
|e -&> raise (Terminating_state success)
|_ -&> raise (Terminating_state error)
let i_found = function
|c -&> raise (State_transfer c_found)
|_ -&> raise (Terminating_state error)
let n_found = function
|i -&> raise (State_transfer i_found)
|_ -&> raise (Terminating_state error)
let start = function
|n -&> raise (State_transfer n_found)
|_ -&> raise (Terminating_state error)
let rec state_machine f lst = match lst with
|[] -&> error
|x::xs -&> try f x with
State_transfer st -&> state_machine st xs
|Terminating_state str -&> str
let match_nice = state_machine start
簡潔一點可以寫成這樣:
exception State_transfer of (char -&> exn)
exception Terminating_state of string
let success = "Success"
let error = "Error"
let c_found = function
|e -&> Terminating_state success
|_ -&> Terminating_state error
let i_found = function
|c -&> State_transfer c_found
|_ -&> Terminating_state error
let n_found = function
|i -&> State_transfer i_found
|_ -&> Terminating_state error
let start = function
|n -&> State_transfer n_found
|_ -&> Terminating_state error
let rec state_machine f lst = match lst with
|[] -&> error
|x::xs -&> match f x with
|State_transfer st -&> state_machine st xs
|Terminating_state str -&> str
|_ -&> error
let match_nice = state_machine start
再把異常換成正常的 type 就和正常的寫法沒什麼區別了(逃
Table-driven
簡單的說說我的思路。考慮自動機需要什麼功能,添加新的轉移路徑,查找路徑,表明起點終點。 實現這些功能即可。
傳統上有兩種辦法,一張大的轉移表,保存全部狀態和轉移路徑;或者面向對象,每個狀態是一個對象。函數式編程,都可以借鑒這兩種方法實現。
轉移表[(int, char, int, start_or_end)],各項分別標記狀態號,路徑的字元,路徑的下個狀態,起點或終點。剩下的就是寫函數來添加、刪除、查找了。最簡單的函數寫法就是haskell的++ !!或scheme的cons car cdr了。 類似面向對象的方法也很簡單。data State= State{sn: int, way: [(char,int)], soe: start_or_end} 要寫這麼幾個函數move, insert, delete, isStart, isEnd。 若是用scheme, define-datatype和cases配合也可以實現。另外,也可以把state實現為函數。insert, delete, move返回的是不同的函數state,(state char)返回移動狀態後的state函數,(state start_end?)返回string表明起點或終點。其實也還是面向對象,不過是另一種實現而已,正是所謂的procedural representation。
非常粗糙的想法,歡迎指正。推薦閱讀:
※haskell 中左箭頭到底做了什麼?
※有沒有對Haskell中理解monad比較好的代碼例子?
※Haskell做APP後端開發能有性能上的飛躍么?