Haskell でオートマトン改

Data.Set を排して唯のリストで書き換えている。骨格はこんな感じ。

  • Ord a 制約が不要となったため、オートマトンを Functor にできた。
  • α-遷移(文字による遷移)とε-遷移の扱いをシンプルに。

NFA から DFA への変換の際、何が起きているかよく理解しよう。そうするときっと Functor であることも生きて奇麗に書けるのではないだろうか。

{-# LANGUAGE NoMonomorphismRestriction #-}

module FA where
import Data.List

-- Data
type Symbol = Char
type Alphabet = [Symbol]

-- Edge fromState toState
data Edge a = Edge a a deriving (Eq)

edgeStates (Edge q q') = [q, q']
edgeMatch r (Edge q q') = r == q

instance Functor Edge where
    fmap f (Edge q q') = Edge (f q) (f q')

instance (Show a) => Show (Edge a) where
    show (Edge q q') = show q ++ " => " ++ show q'

-- LEdge Edge Symbol
data LEdge a = LEdge (Edge a) Symbol deriving (Eq)
labelEdge (LEdge e c) = e
lMatch r c' (LEdge e c) = (c == c') && edgeMatch r e

instance Functor LEdge where
    fmap f (LEdge e c) = LEdge (fmap f e) c

instance (Show a) => Show (LEdge a) where
    show (LEdge (Edge q q') c) = show q ++ " " ++ show c ++ " => " ++ show q'

-- Type class Directed
class Directed d where
    initial :: d a -> a
    terminal :: d a -> a

instance Directed Edge where
    initial (Edge q _) = q
    terminal (Edge _ q') = q'

instance Directed LEdge where
    initial (LEdge e _) = initial e
    terminal (LEdge e _) = terminal e

-- Type class Reversal
class Reversal a where
    rev :: a -> a

instance Reversal ([] a) where
    rev = reverse

instance Reversal (Edge a) where
    rev (Edge q q') = Edge q' q

instance Reversal (LEdge a) where
    rev (LEdge e c) = LEdge (rev e) c

-- FA
type Transition a = [LEdge a]
type ETransition a = [Edge a]
-- Finite Automaton
data FA a = FA { states :: [a],
                 alphabet :: Alphabet,
                 trans :: Transition a,
                 etrans :: ETransition a,
                 starts :: [a],
                 finals :: [a] }

-- Constructor
makeDFA qs cs ts q0 fs = FA (nub qs) (nub cs) (nub ts) [] [q0] (nub fs)
makeNFA qs cs ts es q0s fs = FA (nub qs) (nub cs) (nub ts) (nub es) (nub q0s) (nub fs)

listCmp xs ys = length (xs `union` ys) == length xs

reachable :: Eq a => FA a -> FA a
reachable (FA qs cs ts es q0s fs) = mkfa (fixpoint' move' q0s listCmp)
    where 
      move rs = nub $ concat [ transits ts rs c | c <- cs ]
      ecl = eclosure es
      move' rs = nub (move rs ++ ecl rs)
      mkfa reach = FA reach cs ts' es' q0s fs'
          where 
            isReach q = q `elem` reach
            ts' = filter (all isReach . edgeStates . labelEdge) ts
            es' = filter (all isReach . edgeStates) es
            fs' = filter isReach fs

-- Query
size :: FA a -> Int
size (FA qs _ _ _ _ _) = length qs
isEFree = null
isComplete :: (Eq a) => FA a -> Bool
isComplete (FA qs cs ts _ _ _) = 
    all (not . null) [ transit ts q c | q <- qs, c <- cs ]
isString :: Eq a => [a] -> [a] -> Bool
isString cs = all (`elem` cs)

-- Operation
transit :: (Eq a) => Transition a -> a -> Symbol -> [a]
transit ts q c = map (terminal . labelEdge) $ filter (lMatch q c) ts
transit1 :: (Eq a) => Transition a -> a -> Symbol -> a
transit1 t q c = head (transit t q c)
transits :: Eq a => Transition a -> [a] -> Symbol -> [a]
transits ts rs c = nub $ concat [ transit ts r c | r <- rs ]
etransit :: (Eq a) => ETransition a -> a -> [a]
etransit e q = map terminal (filter (edgeMatch q) e)
-- Query
isDet :: Eq a => FA a -> Bool
isDet (FA qs cs ts es q0s fs) =
    length q0s <= 1 &&
    isEFree es &&
    all (<= 1) [ length (transit ts q c) | q <- qs, c <- cs ]

-- Operation
mapState f (FA qs cs ts es q0s fs) = FA qs' cs ts' es' q0s' fs'
    where
      qs' = map f qs
      ts' = map (fmap f) ts
      es' = map (fmap f) es
      q0s' = map f q0s
      fs' = map f fs

instance Functor FA where
    fmap = mapState

revFA (FA qs cs ts es q0s fs) = FA qs cs (map rev ts) (map rev es) fs q0s

instance Reversal (FA a) where
    rev = revFA

rename :: (Eq a1, Eq a2) => [a2] -> FA a1 -> FA a2
rename as fa@(FA qs _ _ _ _ _) = fmap conv fa
    where
      conv q = case lookup q (zip qs as) of
                 Just x -> x

gennames :: String -> [String]
gennames a = map (\x -> a ++ show x) [0..]
rename' :: (Eq a) => FA a -> FA String
rename' = rename (gennames "q")

-- return DFA
det :: Eq a => FA a -> FA [a]
det m@(FA qs cs ts es q0s fs) = iter di (notmarked di) []
    where
      di = [ (eclosure es q0s, False) ]
      isMarked (_, b) = b
      removeMark = map fst
      notmarked xs = removeMark (filter (not . isMarked) xs)
      maybeAdd us ds | us `elem` removeMark ds = ds
                     | otherwise = (us, False):ds
      ecl = eclosure es
      move = transits ts
      mark x xs =  (x, True): delete (x, False) xs
      q0s' = ecl q0s
      iter ds [] ts' = FA qs' cs ts' [] [q0s'] fs'
          where
            qs' = removeMark ds
            fs' = filter (any (`elem` fs)) qs'
      iter ds (t:ts) ts' = iter ds' (notmarked ds') ts''
          where
            ds' = foldl (\s c -> maybeAdd (ecl (move t c)) s) (mark t ds) cs
            ts'' = foldl (flip (:)) ts' [ LEdge (Edge t (ecl (move t c))) c | c <- cs ]

-- Minimization
minimize = det . reachable . rev . reachable . det . reachable . rev

-- Accept
accept :: (Eq a) => FA a -> [Symbol] -> Bool
-- DFA
accept m@(FA qs cs ts es q0s fs) str
    | isDet m && isString cs str = foldl (transit1 ts) (head q0s) str `elem` fs
-- NFA
accept m@(FA qs cs ts es q0s fs) str
    | isString cs str =
        any (`elem` fs) (fn (eclosure es q0s) str)
            where
              fn = foldl (\s c -> eclosure es (transits ts s c))