{-# LANGUAGE UnicodeSyntax, FlexibleInstances #-} module Data.MarkovAlgo (Algo, Rule (..), Var (..), antecedent, consequent, constructor, expand, parseRule, buildAlgo, runMarkov) where import Data.List -- | Markov's algorithm itself type Algo c = [Rule c] -- | One rule in algorithm data Rule c = [c] :-> [c] -- ^ Non-terminating rule | [c] :->. [c] -- ^ Terminating rule deriving (Eq) instance Show (Rule Char) where show (a :-> s) = a ++ " → " ++ s show (a :->. s) = a ++ " →. " ++ s instance Show (Rule (Var Char)) where show (a :-> s) = show a ++ " → " ++ show s show (a :->. s) = show a ++ " →. " ++ show s -- | Get antecedent of rule antecedent ∷ Rule c → [c] antecedent (xs :-> _) = xs antecedent (xs :->. _) = xs -- | Get consequent of rule consequent ∷ Rule c → [c] consequent (_ :-> ys) = ys consequent (_ :->. ys) = ys -- | Get data constructor of Rule constructor ∷ Rule c → ([d] → [d] → Rule d) constructor (_ :-> _) = (:->) constructor (_ :->. _) = (:->.) -- | Variable for rules data Var α = L α -- ^ Literal char | V Int -- ^ Variable with given number instance Show (Var Char) where show (L c) = [c] show (V c) = show c isChar ∷ Var α → Bool isChar (L _) = True isChar _ = False isVar ∷ Var α → Bool isVar = not . isChar -- | Expand algorithm with variables into algorithm without variables expand ∷ Eq α ⇒ [α] -- ^ Alphabet → Algo (Var α) -- ^ Algorithm with variables → Algo α expand 𝔞 = concatMap (expandRule 𝔞) expandRule ∷ Eq α ⇒ [α] → Rule (Var α) → [Rule α] expandRule 𝔞 rule = nub $ map toChar (expandRule' 𝔞 rule) toChar ∷ Rule (Var α) → Rule α toChar rule = (constructor rule) (toChar' $ antecedent rule) (toChar' $ consequent rule) where toChar' ∷ [Var α] → [α] toChar' = map toChar'' toChar'' (L c) = c toChar'' (V _) = error "Internal error" expandRule' ∷ [α] → Rule (Var α) → [Rule (Var α)] expandRule' 𝔞 rule = [(constructor rule) a c | (a,c) ← expandString 𝔞 (antecedent rule) (consequent rule)] expandString ∷ [α] → [Var α] → [Var α] → [([Var α], [Var α])] expandString 𝔞 as cs = map (subst as cs) $ mapM (const 𝔞) [1..n] where n = length (filter isVar (as ++ cs)) subst ∷ [Var α] → [Var α] → [α] → ([Var α], [Var α]) subst as cs xs = (subst' as xs, subst' cs xs) where subst' [] _ = [] subst' (L c: vs) cs = L c: subst' vs cs subst' (V n: vs) cs = L (cs !! n): subst' vs cs subst' (V _: _) [] = error "Internal error: too few arguments in `subst'!" parseString ∷ Eq α ⇒ [α] → [α] → [Var α] parseString xs s = map toVar s where toVar c = case elemIndex c xs of Nothing → L c Just n → V n -- | Create generic Rule from concrete Rule parseRule ∷ Eq α ⇒ [α] -- ^ Names of variables → Rule α -- ^ Concrete rule → Rule (Var α) parseRule xs rule = (constructor rule) (parseString xs $ antecedent rule) (parseString xs $ consequent rule) replace ∷ Eq α ⇒ [α] → [α] → [α] → [α] replace _ _ [] = [] replace old new s@(c:cs) | old `isPrefixOf` s = new ++ (drop (length old) s) | otherwise = c: replace old new cs applyRule ∷ Eq α ⇒ Rule α → [α] → (Maybe [α], Bool) applyRule rule s | a `isInfixOf` s = (Just $ replace a c s, shouldStop rule) | otherwise = (Nothing, shouldStop rule) where a = antecedent rule c = consequent rule shouldStop ∷ Rule c → Bool shouldStop (_ :-> _) = False shouldStop (_ :->. _) = True -- | Run concrete (without variables) Markov's algorithm runMarkov ∷ Eq α ⇒ Algo α -- ^ Algorithm itself → [α] -- ^ Start string → [α] runMarkov algo s = runMarkov' algo algo s where runMarkov' :: Eq α ⇒ Algo α → Algo α → [α] → [α] runMarkov' _ [] s = s runMarkov' algo (r:rs) s = case applyRule r s of (Just res, False) → runMarkov' algo algo res (Just res, True) → res (Nothing, _) → runMarkov' algo rs s -- | Build concrete algo from simple text description with variables buildAlgo ∷ Eq α ⇒ [α] -- ^ Alphabet → [α] -- ^ Names of variables → Algo α -- ^ Description of algorithm → Algo α buildAlgo 𝔞 vs algo = expand 𝔞 $ map (parseRule vs) algo