Implement Dfa using Fa

This commit is contained in:
Joscha 2019-10-30 15:11:08 +00:00
parent 33ed11abc4
commit e273c8bc75
3 changed files with 62 additions and 111 deletions

View file

@ -1,21 +1,12 @@
module Rextra.Dfa ( {-# LANGUAGE FlexibleInstances #-}
-- * Deterministic Finite Automaton {-# LANGUAGE MultiParamTypeClasses #-}
Dfa {-# LANGUAGE NamedFieldPuns #-}
, State(..)
, StateMap module Rextra.Dfa
-- ** Constructing ( Dfa
, dfa , dfa
, dfa' , State(..)
-- ** Properties
, stateMap
, entryState
, exitStates
, transitionsByState , transitionsByState
-- ** Executing
, transition
, execute
-- ** Renaming
, rename
) where ) where
import Data.List import Data.List
@ -23,105 +14,40 @@ import qualified Data.Map.Strict as Map
import qualified Data.Set as Set import qualified Data.Set as Set
import Data.Tuple import Data.Tuple
import Rextra.Fa
import Rextra.Util import Rextra.Util
{- -- State stuff
- Types
-}
data Dfa s t = Dfa
{ stateMap :: StateMap s t
, entryState :: s
} deriving (Show)
getState :: (Ord s) => Dfa s t -> s -> State s t
getState dfa s = stateMap dfa Map.! s
exitStates :: (Ord s) => Dfa s t -> Set.Set s
exitStates dfa = Set.fromList
. map fst
. filter (accepting . snd)
. Map.assocs
$ stateMap dfa
data State s t = State data State s t = State
{ transitions :: Map.Map t s { transitions :: Map.Map t s
, defaultTransition :: s , defaultTransition :: s
, accepting :: Bool
} deriving (Show) } deriving (Show)
type StateMap s t = Map.Map s (State s t) instance FaState State where
canReach State{transitions, defaultTransition} =
Set.fromList $ defaultTransition : Map.elems transitions
fromMonoidalList :: (Monoid m, Ord k) => [(k, m)] -> Map.Map k m transitionsByState :: (Ord s, Ord t) => State s t -> Map.Map s (Set.Set t)
fromMonoidalList = foldl' insertMonoidal Map.empty transitionsByState = groupByFirst . map swap . Map.assocs . transitions
where
insertMonoidal :: (Monoid m, Ord k) => Map.Map k m -> (k, m) -> Map.Map k m
insertMonoidal map (k, m) = Map.insertWith mappend k m map
groupByFirst :: (Ord a, Ord b) => [(a, b)] -> Map.Map a (Set.Set b) -- Dfa stuff
groupByFirst pairs =
let prepared = map (\(a, b) -> (a, Set.singleton b)) pairs
in fromMonoidalList prepared
transitionsByState :: (Ord s, Ord t) => Map.Map t s -> Map.Map s (Set.Set t) type Dfa s t = Fa State s t
transitionsByState = groupByFirst . map swap . Map.assocs
{- instance (Ord s) => Executable (Fa State s) s where
- Constructing startState = entryState
-} transition = dfaTransition
accepts a s = s `Set.member` exitStates a
integrityCheck :: (Ord s) => Dfa s t -> Bool dfaTransition :: (Ord s, Ord t) => Dfa s t -> s -> t -> s
integrityCheck dfa = dfaTransition a s t =
let states = Map.elems $ stateMap dfa let state = getState a s
transitionStates = concatMap (Map.elems . transitions) states
defaultTransitionStates = map defaultTransition states
referencedStates = Set.fromList $ concat [[entryState dfa], transitionStates, defaultTransitionStates]
in referencedStates `Set.isSubsetOf` Map.keysSet (stateMap dfa)
dfa :: (Ord s) => StateMap s t -> s -> Maybe (Dfa s t)
dfa stateMap entryState =
let myDfa = Dfa{stateMap=stateMap, entryState=entryState}
in if integrityCheck myDfa then Just myDfa else Nothing
dfa' :: (Ord s, Ord t) => [(s, [(t, s)], s, Bool)] -> s -> Maybe (Dfa s t)
dfa' states entryState =
let stateList = map (\(s, ts, dt, a) -> (s, State (Map.fromList ts) dt a)) states
in dfa (Map.fromList stateList) entryState
{-
- Executing
-}
transition :: (Ord s, Ord t) => Dfa s t -> s -> t -> s
transition dfa s t =
let state = getState dfa s
in case transitions state Map.!? t of in case transitions state Map.!? t of
(Just nextState) -> nextState (Just nextState) -> nextState
Nothing -> defaultTransition state Nothing -> defaultTransition state
execute :: (Ord s, Ord t) => Dfa s t -> [t] -> Bool dfa :: (Ord s, Ord t) => [(s, [(t, s)], s)] -> s -> [s] -> Maybe (Dfa s t)
execute dfa tokens = dfa states entryState exitStates =
let finalState = foldl' (transition dfa) (entryState dfa) tokens let stateList = map (\(s, ts, dt) -> (s, State (Map.fromList ts) dt)) states
in accepting $ getState dfa finalState in fa (Map.fromList stateList) entryState (Set.fromList exitStates)
{-
- Renaming
-}
renameState :: (Ord s, Ord t) => State s t -> Rename s (State Int t)
renameState state = do
newTransitions <- renameValues getName $ transitions state
newDefaultTransition <- getName $ defaultTransition state
pure $ State { transitions = newTransitions
, defaultTransition = newDefaultTransition
, accepting = accepting state
}
renameAssoc :: (Ord s, Ord t) => (s, State s t) -> Rename s (Int, State Int t)
renameAssoc (name, state) = (,) <$> getName name <*> renameState state
rename :: (Ord s, Ord t) => Dfa s t -> Dfa Int t
rename dfa = doRename $ do
newStateMap <- renameMap renameAssoc $ stateMap dfa
newEntryState <- getName $ entryState dfa
pure $ Dfa { stateMap = newStateMap, entryState = newEntryState }

View file

@ -4,8 +4,9 @@
-- | This module contains ways to represent finite automata and their -- | This module contains ways to represent finite automata and their
-- execution/evaluation. -- execution/evaluation.
module Rextra.Fa module Rextra.Fa (
( FaState(..) -- * Finite automata
FaState(..)
, Fa , Fa
, fa , fa
, stateMap , stateMap
@ -13,6 +14,7 @@ module Rextra.Fa
, exitStates , exitStates
, states , states
, getState , getState
-- * Executing automata
, Executable(..) , Executable(..)
, transitionAll , transitionAll
, execute , execute
@ -22,11 +24,15 @@ import Data.List
import qualified Data.Map as Map import qualified Data.Map as Map
import qualified Data.Set as Set import qualified Data.Set as Set
{-
- Finite automata
-}
-- | The state of a finite automaton. -- | The state of a finite automaton.
class FaState state where class FaState state where
-- | All the states that can be reached (by any sort of transition) -- | All the states that can be reached (by any sort of transition)
-- from this state. -- from this state.
canReach :: state s t -> Set.Set s canReach :: (Ord s) => state s t -> Set.Set s
-- | A finite automaton. -- | A finite automaton.
data Fa state s t = Fa data Fa state s t = Fa
@ -38,7 +44,7 @@ data Fa state s t = Fa
, exitStates :: Set.Set s , exitStates :: Set.Set s
-- ^ The automaton's accepting states, i. e. the states that -- ^ The automaton's accepting states, i. e. the states that
-- determine whether the automaton accepts a certain word. -- determine whether the automaton accepts a certain word.
} } deriving (Show)
-- | @'states' fa@ are the identifiers of all states contained in @fa@. -- | @'states' fa@ are the identifiers of all states contained in @fa@.
states :: Fa state s t -> Set.Set s states :: Fa state s t -> Set.Set s
@ -67,19 +73,23 @@ fa stateMap entryState exitStates =
let potentialFa = Fa{stateMap, entryState, exitStates} let potentialFa = Fa{stateMap, entryState, exitStates}
in if integrityCheck potentialFa then Just potentialFa else Nothing in if integrityCheck potentialFa then Just potentialFa else Nothing
{-
- Executing
-}
-- | A special type class for automata that can be executed. These -- | A special type class for automata that can be executed. These
-- automata must not necessarily be finite. -- automata must not necessarily be finite.
class Executable a execState where class Executable a execState where
-- | The state at which execution of the automaton begins. -- | The state at which execution of the automaton begins.
startState :: a s t -> execState startState :: a t -> execState
-- | A function that determines the automaton's next state based on -- | A function that determines the automaton's next state based on
-- a token. -- a token.
transition :: a s t -> execState -> t -> execState transition :: (Ord t) => a t -> execState -> t -> execState
-- | Whether the automaton acceps -- | Whether the automaton accepts the execution state.
accepts :: a s t -> execState -> Bool accepts :: a t -> execState -> Bool
-- | Perform all transitions corresponding to a word (or list) of tokens, in order. -- | Perform all transitions corresponding to a word (or list) of tokens, in order.
transitionAll :: (Executable a execState) => a s t -> execState -> [t] -> execState transitionAll :: (Executable a execState, Ord t) => a t -> execState -> [t] -> execState
transitionAll a = foldl' (transition a) transitionAll a = foldl' (transition a)
-- | Like 'transitionAll', starting with the automaton's 'startState'. -- | Like 'transitionAll', starting with the automaton's 'startState'.
@ -88,5 +98,5 @@ transitionAll a = foldl' (transition a)
-- 'accepts' like this: -- 'accepts' like this:
-- --
-- > a `accepts` execute a w -- > a `accepts` execute a w
execute :: (Executable a execState) => a s t -> [t] -> execState execute :: (Executable a execState, Ord t) => a t -> [t] -> execState
execute a = transitionAll a (startState a) execute a = transitionAll a (startState a)

View file

@ -10,10 +10,14 @@ module Rextra.Util
, renameMap , renameMap
, renameKeys , renameKeys
, renameValues , renameValues
-- * Grouping
, fromMonoidalList
, groupByFirst
) where ) where
import Control.Monad import Control.Monad
import Control.Monad.Trans.State import Control.Monad.Trans.State
import Data.List
import qualified Data.Map as Map import qualified Data.Map as Map
import qualified Data.Set as Set import qualified Data.Set as Set
@ -54,3 +58,14 @@ renameKeys f = renameMap (\(k, v) -> (,v) <$> f k)
renameValues :: (Ord k) => (v1 -> Rename n v2) -> Map.Map k v1 -> Rename n (Map.Map k v2) renameValues :: (Ord k) => (v1 -> Rename n v2) -> Map.Map k v1 -> Rename n (Map.Map k v2)
renameValues f = renameMap (\(k, v) -> (k,) <$> f v) renameValues f = renameMap (\(k, v) -> (k,) <$> f v)
fromMonoidalList :: (Monoid m, Ord k) => [(k, m)] -> Map.Map k m
fromMonoidalList = foldl' insertMonoidal Map.empty
where
insertMonoidal :: (Monoid v, Ord k) => Map.Map k v -> (k, v) -> Map.Map k v
insertMonoidal m (k, v) = Map.insertWith mappend k v m
groupByFirst :: (Ord a, Ord b) => [(a, b)] -> Map.Map a (Set.Set b)
groupByFirst pairs =
let prepared = map (\(a, b) -> (a, Set.singleton b)) pairs
in fromMonoidalList prepared