Python 用Haskell表示非确定性有限状态机模拟器
我正在Udacity上学习“编程语言”,并试图用Haskell来表示问题集。答案是用Python编写的:Python 用Haskell表示非确定性有限状态机模拟器,python,haskell,recursion,functional-programming,monads,Python,Haskell,Recursion,Functional Programming,Monads,我正在Udacity上学习“编程语言”,并试图用Haskell来表示问题集。答案是用Python编写的: edges = {(1,"a") : [2,3] ,(2,"a") : [2] ,(3,"b") : [3,4] ,(4,"c") : [5]} accepting = [2,5] def nfsmSim(string, current, edges, accepting): if string == "": ret
edges = {(1,"a") : [2,3]
,(2,"a") : [2]
,(3,"b") : [3,4]
,(4,"c") : [5]}
accepting = [2,5]
def nfsmSim(string, current, edges, accepting):
if string == "":
return current in accepting
else:
letter = string[0]
key = (current, letter)
if key in edges:
rest = string[1:]
states = edges[key]
for state in states:
if nfsmSim(rest, state, edges, accepting):
return True
return False
启动状态始终是第一个状态,即当前=1
接受诸如“aaa”
或“abc”
之类的字符串,同时接受或拒绝“abb”
或“aabc”
我尝试使用Haskell重写:
nfsmSim [] c _ = [c]
nfsmSim xs c es = [concat $ nfsmSim (tail xs) s es | (k,ss) <- es, s <- ss, x <- xs, k==(c,x)]
nfsmSim[]c[c]
nfsmSim xs c es=[concat$nfsmSim(tail xs)s es |(k,ss)让我们先考虑一下类型。您的Python函数或多或少有以下类型:
type State=Int
类型映射kv=[(k,v)]
nfsmSim::String->State->Map(Int,Char)[State]->[State]->Bool
nfsmSim字符串当前边缘接受=…
我们可以对空字符串大小写使用模式匹配:
nfsmSim::String->State->Map(Int,Char)[State]->[State]->Bool
nfsmSim“”当前uu接受=当前`元素`接受
对于非空的情况,我们执行与Python代码相同的操作:
nfsmSim(x:xs)当前边缘接受=
让rest=xs
状态=[s |(k,v)b->[a])->(a->Bool->[b]->a->Bool
nfsmSim高级接受字符串电流=go字符串电流
哪里
go[]c=接受c
go(x:xs)c=any(go-xs)(前进cx)
顺便说一下,最后一个变量仍然可以使用“边”和“接受”
nfsmSimAccept字符串当前边缘接受=
让我们接受c=c`elem`接受
advancecx=[s |(k,v)这是我的Haskell-ish方法:
我们可以使用haskell的Data.Set和Data.Map库来表示我们的状态机
import qualified Data.Map as M
import qualified Data.Set as S
让我们为状态机定义一些数据类型:
type State = Int
type Edge = (State, Char)
type Machine = (M.Map Edge (S.Set State), S.Set State)
我们这样定义机器:
myMachine :: Machine
myMachine = (M.fromList
[ ((1, 'a'), S.fromList [2, 3])
, ((2, 'a'), S.fromList [2 ])
, ((3, 'b'), S.fromList [3, 4])
, ((4, 'c'), S.fromList [5 ])
] , S.fromList [2, 5])
我们可以这样运行机器:
runMachine :: String -> Machine -> State -> Bool
runMachine "" (_, acceptingStates) currentState =
S.member currentState acceptingStates
runMachine (ch:rest) machine@(edges, _) currentState =
case M.lookup (currentState, ch) edges of
Nothing -> False
Just nextStates ->
or $ S.map (runMachine rest machine) nextStates
由于函数返回一个Bool
,因此没有很好的理由使用monad或do表示法。但是,如果我们使用类型Maybe()
代替Bool
,其中只是()
表示真
和无
表示假首先,我知道,没有“非有限状态机”这样的东西。从你写的判断,我意识到它是关于“非确定性有限自动机(NFA)”
第一种变体
nfa :: String -> Int -> [((Int, Char), [Int])] -> [Int] -> Bool
nfa [] cur _ acc = cur `elem` acc
nfa (c:rest) cur edges acc
| Just states <- lookup (cur, c) edges = any (\state -> nfa rest state edges acc) states
| otherwise = False
edges =
[ ((1, 'a'), [2, 3])
, ((2, 'a'), [2])
, ((3, 'b'), [3, 4])
, ((4, 'c'), [5])
]
accepting = [2, 5]
main = do
print $ nfa "aaa" 1 edges accepting
print $ nfa "abc" 1 edges accepting
print $ nfa "abb" 1 edges accepting
print $ nfa "aabc" 1 edges accepting
第二种变体:
import Control.Monad
import Data.Maybe
nfa2 :: String -> Int -> [((Int, Char), [Int])] -> [Int] -> [Int]
nfa2 [] cur _ acc = guard (cur `elem` acc) >> return cur
nfa2 (c:rest) cur edges acc = do
state <- fromMaybe mzero $ lookup (cur, c) edges
nfa2 rest state edges acc
edges =
[ ((1, 'a'), [2, 3])
, ((2, 'a'), [2])
, ((3, 'b'), [3, 4])
, ((4, 'c'), [5])
]
accepting = [2, 5]
main = do
print $ nfa2 "aaa" 1 edges accepting
print $ nfa2 "abc" 1 edges accepting
print $ nfa2 "abb" 1 edges accepting
print $ nfa2 "aabc" 1 edges accepting
如果可能的话,你能给这个练习添加一个链接吗?@ZetaSet
太过分了。不需要“结束”状态是一个排序集。感谢你的解决方案。我对Haskell的理解水平很低,所以你介绍的许多概念希望在以后的过程中更有意义。感谢你纠正我的草率问题!我编辑了标题。我的想法对吗只是状态不,这只是Haskell 2010语法“Patter guard”()感谢您提供的解决方案。您的回答正好符合我的能力,我从您的数据方法中学到了很多。顺便说一句,当我复制您的第一个解决方案并在haskell repl中运行它时,我得到了无法将类型
Integer'与Int'匹配的结果;预期类型:[State];实际类型:[Integer]
。我将Int
替换为Integer
,然后进行编译。有什么原因吗?@poton你在什么地方使用了Integer
?我复制了原样的解决方案。可能是版本(7.6.3)最终的解决方案也遭到了反对,直到我将b
替换为Char
。我意识到类型系统有助于澄清我混乱的想法,但有时它的错误消息似乎有点迟钝。我想我必须更深入地研究这些消息,或者可能有一个开关这可以给出一个更详细的解释吗?@potong hm.这超出了这个问题的范围,但是你能把你的完整代码作为要点或粘贴库或类似的东西发布吗?如果没有你周围的代码,很难看出错误可能在哪里。经过一点修改,我意识到我已经在编译的c中包含了边缘
和接受
ode没有为任何一个提供类型。我相信编译器将Integer
分配给了这两个。通过预先为这两个问题指定一个类型,问题就解决了。谢谢你的耐心,每天我都在学习思考慢一点但更清晰一点。
import Control.Monad
import Data.Maybe
nfa2 :: String -> Int -> [((Int, Char), [Int])] -> [Int] -> [Int]
nfa2 [] cur _ acc = guard (cur `elem` acc) >> return cur
nfa2 (c:rest) cur edges acc = do
state <- fromMaybe mzero $ lookup (cur, c) edges
nfa2 rest state edges acc
edges =
[ ((1, 'a'), [2, 3])
, ((2, 'a'), [2])
, ((3, 'b'), [3, 4])
, ((4, 'c'), [5])
]
accepting = [2, 5]
main = do
print $ nfa2 "aaa" 1 edges accepting
print $ nfa2 "abc" 1 edges accepting
print $ nfa2 "abb" 1 edges accepting
print $ nfa2 "aabc" 1 edges accepting
[2]
[5]
[]
[]