在Haskell中表示非确定性有限状态机模拟器

时间:2016-11-22 11:11:15

标签: 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 == "":
        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

起始状态始终是第一个状态,即current = 1

"aaa""abc""abb"接受"aabc"nfsmSim [] c _ = [c] nfsmSim xs c es = [concat $ nfsmSim (tail xs) s es | (k,ss) <- es, s <- ss, x <- xs, k==(c,x)] 等字符串。

我尝试使用Haskell重写:

filter

我想返回一个整数列表,它表示输入字符串末尾的最后一个状态,然后any表示接受状态,并使用True得到最终False }或wholemeal

我意识到这可能不是Haskell的做法,并且可能有更好的do解决方案。然而作为一个初学者,我正在努力解决mondadic机制,很可能是这个问题的递归性质。

请指出正确的方向,可能使用 srand((unsigned int) time(NULL)); algorytm a; fstream wynik; wynik.open("result.txt",ios::out | ios::app); for(int i=0; i<how_test; i++){ //how many tests write(how_n); //how many n in my file, and create file a.read() //read from file (n, and weight / val) time_start(); a.sort(); //I sort it a.intoKnapsack(0, 0.0, 0.0); //my function above, so I give here a 3x to do it properly over and over in loop get_time(); //stop time measurement+=get_time(); result<<get_time()<<" s."<<endl; //just for } 符号而非列表理解。

3 个答案:

答案 0 :(得分:4)

首先,据我所知,没有“非有限状态机”这样的东西。从你写的内容来看,我意识到它是关于“非确定性有限自动机(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

输出将是:

True
True
False
False

第二种变体:

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]
[]
[]

答案 1 :(得分:2)

让我们首先考虑类型。您的Python函数具有以下类型,或多或少:

type State   = Int
type Map k v = [(k,v)]

nfsmSim :: String -> State -> Map (Int, Char) [State] -> [State] -> Bool
nfsmSim string current edges accepting = …

我们可以使用模式匹配来处理空字符串:

nfsmSim :: String -> State -> Map (Int, Char) [State] -> [State] -> Bool
nfsmSim "" current _ accepting = current `elem` accepting

对于非空案例,我们的做法与Python代码相同:

nfsmSim (x:xs) current edges accepting =
    let rest   = xs
        states = [s | (k,v) <- edges, k == (current,x), s <- v]
    in or [nfsmSim rest state edges accepting | state <- states]

但是,这并不容易。相反,让我们将nfsmSim写为高阶函数并更改参数的顺序:

nfsmSim :: (State -> Char -> [State])
        -> (State -> Bool)
        -> String
        -> State
        -> Bool

现在,我们必须提供一个函数来返回给定状态和字符的状态列表(而不是接受状态列表),而不是边缘列表,我们提供一个函数返回关于这些州的True

对于空字符串的情况,没有太多变化:

nfsmSim advance accept "" current = accept current

我们只是使用State -> Bool来检查我们当前的状态是否可以接受。

但是,现在我们的StatenfsmSim的最后一个参数,我们可以使用currying来使用您的any方法:

nfsmSim advance accept (x:xs) current = 
    any (nfsmSim advance accept xs) (advance current x)

请注意,随身携带所有参数都很笨拙。你通常会为此写一个工人:

nfsmSim :: (a -> b -> [a]) -> (a -> Bool) -> [b] -> a -> Bool
nfsmSim advance accept string current = go string current
  where
    go []     c = accept c
    go (x:xs) c = any (go xs) (advance c x)

顺便说一句,您仍然可以使用“edge”和“接受”最后一个变体

nfsmSimAccept string current edges accepting =
   let accept  c   = c `elem` accepting
       advance c x = [s | (k,v) <- edges, k == (c,x), s <- v]
   in nfsmSim advance accept string current

表明高阶函数更灵活。

答案 2 :(得分:2)

这是我的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-notation。但是,如果我们使用Maybe ()类型代替Bool,其中Just ()代表TrueNothing代表False,则可以使用此解决方案。