So the problem I'm working on matching a pattern to a list, such like this:
match "abba" "redbluebluered" -> True
or
match "abba" "redblueblue" -> False
, etc. I wrote up an algorithm that works, and I think it's reasonable understandable, but I'm not sure if there's a better way to do this without explicit recursion.
import Data.HashMap.Strict as M
match :: (Eq a, Eq k, Hashable k) => [k] -> [a] -> HashMap k [a] -> Bool
match [] [] _ = True
match [] _ _ = False
match _ [] _ = False
match (p:ps) s m =
case M.lookup p m of
Just v ->
case stripPrefix v s of
Just post -> match ps post m
Nothing -> False
Nothing -> any f . tail . splits $ s
where f (pre, post) = match ps post $ M.insert p pre m
splits xs = zip (inits xs) (tails xs)
I would call this like match "abba" "redbluebluered" empty
. The actual algorithm is simple. The map contains the patterns already matched. At the end it is [a - > "red", b -> "blue"]. If the next pattern is one we've seen before, just try matching it and recurse down if we can. Otherwise fail and return false.
If the next pattern is new, just try mapping the new pattern to every single prefix in the string and recursing down.
This is very similar to a parsing problem, so let's take a hint from the parser monad:
match
should return a list of all of the possible continuations of the parseTo see where we are headed, let's suppose we have this magic monad. Attempting to match "abba" against a string will look like:
It turns out this monad is the State monad over the List monad. The List monad provides for backtracking and the State monad carries the current assignments and input around.
Here's the code:
Try, for instance:
Another thing to point out is that code which transforms a string like "abba" to the monadic value
do var'a'; var'b'; var 'b'; var 'a'
is simply:Update: As @Sassa NF points out, to match the end of input you'll want to define:
and then insert it into the monad: