-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathAutomata.hs
170 lines (136 loc) · 5.11 KB
/
Automata.hs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
{-# LANGUAGE MultiParamTypeClasses, FunctionalDependencies #-}
module Automata (
Automata(..)
,State
,String
,DFA
,NFA
,process
,execute
,dfa
,nfa
) where
import Control.Monad.Writer
import Data.Maybe
import Data.List
import Convergence
{- Automata class -}
class (Monad m) => Automata a m | a -> m, m -> a where
-- | 'initial' returns the initial(s) state(s).
initial :: a -> m State
-- | 'isFinal' returns the final predicate.
isFinal :: a -> (State -> Bool)
-- | 'delta' returns the delta function.
delta :: a -> (State -> Alpha -> m State)
-- | 'accept' checks if a word is accepted.
accept :: a -> [Alpha] -> Bool
-- | 'alpha' returns the alphabet
alpha :: a -> [Alpha]
-- | 'toNFA' converts an automaton to a NFA.
toNFA :: a -> NFA
-- | 'states' returns the set of states.
states :: a -> [State]
states = listStates . toNFA
instance Automata DFA Maybe where
initial = initialDFA
isFinal = isFinalDFA
delta = deltaDFA
alpha = alphaDFA
accept dfa = maybe False (isFinal dfa) . process dfa
toNFA (DFA i f t a) = NFA (maybeToList i) f (\s c -> maybeToList $ t s c) a
instance Automata NFA [] where
initial = initialNFA
isFinal = isFinalNFA
delta = deltaNFA
alpha = alphaNFA
accept nfa = any (isFinal nfa) . process nfa
toNFA = id
{- Basic types for automata -}
-- | 'State' is a type synonym of String for automata states.
type State = String
-- | 'Alpha' is a type synonym of String for automata symbols.
type Alpha = Char
-- | 'DFA' is the type for deterministic finite automata. Its constructor
-- takes the initial state, a predicate which defines final states and the
-- transition function. The transition function operates inside the Maybe
-- monad for error handling.
data DFA = DFA {
initialDFA :: Maybe State,
isFinalDFA :: State -> Bool,
deltaDFA :: State -> Alpha -> Maybe State,
alphaDFA :: [Alpha]
}
-- | 'NFA' is the type for non-deterministic finite automata. It has one
-- constructor which takes the set of initial states, a predicate which
-- defines final states and the transition function. The transition
-- function operates inside the List Monad for non-determinism.
data NFA = NFA {
initialNFA :: [State],
isFinalNFA ::State -> Bool,
deltaNFA ::State -> Alpha -> [State],
alphaNFA :: [Alpha]
}
{- Auxiliary functions -}
-- | 'addLog' creates a list of old states. Useful in logging functions.
addLog :: [State] -> State -> (State, [State])
addLog old new = (new, old)
-- | 'addFinal' adds final state to the list.
addFinal :: (State, [State]) -> [State]
addFinal (s,ss) = ss ++ [s]
-- | 'foldI' is equivalent to 'foldM' with its initial value inside a monad.
foldI :: (Monad m) => (a -> b -> m a) -> m a -> [b] -> m a
foldI f i xs = i >>= \x -> foldM f x xs
-- | 'initialW' returns initial state(s) inside minimum WriterT context.
initialW :: Automata a m => a -> WriterT [State] m State
initialW fa = WriterT $ liftM (addLog []) (initial fa)
-- | 'epsilon' is an auxiliary function used to calculate the epsilon closure.
epsilon :: NFA -> State -> [State]
epsilon fa s = delta fa s 'ɛ'
-- | 'step' is an auxiliary function used to calculate the epsilon closure.
step :: NFA -> ([State], [State]) -> ([State], [State])
step fa (old,acc) =
let new = filter (flip notElem acc) (concatMap (epsilon fa) old)
in (new, acc ++ new)
-- | 'closure' calculates a state epsilon closure.
closure :: NFA -> State -> [State]
closure fa s = nub $ snd $ until (null . fst) (step fa) ([s],[s])
-- | 'listStates' returns the set of states of a NFA.
listStates :: NFA -> [State]
listStates nfa = stabilize (nub . concat . (map (newStates nfa))) (initial nfa)
-- | 'newStates' is an auxiliary function used to calculate the set of states.
newStates :: NFA -> State -> [State]
newStates nfa s = s : concat [(delta nfa) s a | a <- (alpha nfa)]
{- Logging and processing functions -}
-- | 'process' returns the final state of a finite automaton given a word.
-- Returns a default value if execution wasn't correct.
process :: (Automata a m) => a -> [Alpha] -> m State
process fa = foldI (delta fa) (initial fa)
-- | 'logt' takes an automaton and returns its delta function inside the
-- WriterT transformer. This allows keeping a log of states.
logt :: (Automata a m) => a -> (State -> Alpha -> WriterT [State] m State)
logt fa old symbol = WriterT $ liftM (addLog [old]) (delta fa old symbol)
-- | 'execute' returns the execution(s) log(s) given a word.
execute :: (Automata a m) => a -> [Alpha] -> m [State]
execute fa = liftM addFinal . runWriterT . foldI (logt fa) (initialW fa)
{- Example -}
t "1" 'A' = Just "2"
t "2" 'A' = Just "3"
t "2" 'B' = Just "1"
t "2" 'C' = Just "3"
t "3" 'D' = Just "5"
t "4" 'A' = Just "2"
t _ _ = Nothing
a = ['A','B','C','D']
i = Just "1"
f = (`elem` ["4","5"])
dfa = DFA i f t a
t' "1" 'A' = ["2","4"]
t' "2" 'A' = ["3"]
t' "2" 'B' = ["1"]
t' "2" 'C' = ["3"]
t' "3" 'D' = ["5"]
t' "4" 'A' = ["2"]
t' _ _ = []
i' = ["1", "2"]
nfa = NFA i' f t' a
main = (print $ execute dfa "AAD") >> (print $ execute nfa "AA")