This is very similar to a parsing problem, so let's take a hint from the parsing monad:
match
should return a list of all possible parsing continuations- If the match fails, it should return an empty list.
- the current set of assignments will consist of the state that must be executed when calculating
To see where we are heading, suppose we have this magic monad. Trying to match "abba" with a string would look like this:
matchAbba = do var 'a' var 'b' var 'b' var 'a' return () -- or whatever you want to return test = runMatch matchAbba "redbluebluered"
It turns out that this monad is the state monad over the List Monad. The list monad provides a countdown, and the state monad carries the current assignment and input.
Here is the code:
import Data.List import Control.Monad import Control.Monad.State import Control.Monad.Trans import Data.Maybe import qualified Data.Map as M import Data.Monoid type Assigns = M.Map Char String splits xs = tail $ zip (inits xs) (tails xs) var p = do (assigns,input) <- get guard $ (not . null) input case M.lookup p assigns of Nothing -> do (a,b) <- lift $ splits input let assigns' = M.insert pa assigns put (assigns', b) return a Just t -> do guard $ isPrefixOf t input let inp' = drop (length t) input put (assigns, inp') return t matchAbba :: StateT (Assigns, String) [] Assigns matchAbba = do var 'a' var 'b' var 'b' var 'a' (assigns,_) <- get return assigns test1 = evalStateT matchAbba (M.empty, "xyyx") test2 = evalStateT matchAbba (M.empty, "xyy") test3 = evalStateT matchAbba (M.empty, "redbluebluered") matches :: String -> String -> [Assigns] matches pattern input = evalStateT monad (M.empty,input) where monad :: StateT (Assigns, String) [] Assigns monad = do sequence $ map var pattern (assigns,_) <- get return assigns
Try for example:
matches "ab" "xyz" -- [fromList [('a',"x"),('b',"y")],fromList [('a',"x"),('b',"yz")],fromList [('a',"xy"),('b',"z")]]
One more note: code that converts a string of type abba to the monadic value do var'a'; var'b'; var 'b'; var 'a'
do var'a'; var'b'; var 'b'; var 'a'
do var'a'; var'b'; var 'b'; var 'a'
, simply:
sequence $ map var "abba"
Update. As @Sassa NF points out, to match the end of the input, you must define:
matchEnd :: StateT (Assigns,String) [] () matchEnd = do (assigns,input) <- get guard $ null input
and then paste it into the monad:
monad = do sequence $ map var pattern matchEnd (assigns,_) <- get return assigns