finding sublist

Andrew J Bromage andrew@bromage.org
Mon, 6 May 2002 17:54:06 +1000


G'day all.

On Mon, May 06, 2002 at 02:15:55PM +1000, Garner, Robin wrote:

> How you would do this in a functional implementation is another question -
> Dijkstra's example is comparing two arrays, and there may be inefficiencies
> translating it to a list-based implementation.

Here's my humble contribution.  It compiles the string to a function
which performs the match, using continuations to handle the failure
transitions.  It's also not a good example of the sort of Haskell
code that you should write.  It's possibly also buggy.

Also note that this returns the list split at the point _after_ the
string is matched, not before.  Altering it to return the point before
is left as an exercise.

Cheers,
Andrew Bromage

--------8<--CUT HERE---8<--------

import List

type PartialMatchFunc m a = [a] -> [a] -> m ([a], [a])

makeMatchFunc :: (Monad m, Eq a) => [a] -> ([a] -> m ([a],[a]))
makeMatchFunc []
  = error "Can't make match func for empty list"
makeMatchFunc xs
  = \ys -> matchfunc [] ys
  where
  	matchfunc = makeMatchFunc' [dofail] (zip xs (overlap xs))
	dofail = \ps xs -> case xs of
				[] -> error "can't match"
				(y:ys) -> matchfunc (y:ps) ys

overlap :: (Eq a) => [a] -> [Int]
overlap str
  = overlap' [0] str
  where
	overlap' prev []
	  = reverse prev
	overlap' prev (x:xs)
	  = let get_o o
		  | o < 2 || str !! (o-2) == x = o
		  | otherwise = get_o (1 + prev !! (length prev - o + 1))
	    in overlap' (get_o (head prev + 1):prev) xs

makeMatchFunc' :: (Monad m, Eq a) => [PartialMatchFunc m a] -> [(a, Int)]
		-> PartialMatchFunc m a
makeMatchFunc' prev []
  = \ps xs -> return (reverse ps, xs)
makeMatchFunc' prev mms@((x,failstate):ms)
  = thisf
  where
	mf = makeMatchFunc' (thisf:prev) ms
	failcont = prev !! (length prev - failstate - 1)
	thisf = \ps xs -> case xs of
				[] -> fail "can't match"
				(y:ys) -> if (x == y) then mf (y:ps) ys
						else failcont ps xs

-- Some tests

type MatchMaybe a = [a] -> Maybe ([a],[a])

ex_abra :: MatchMaybe Char
ex_abra = makeMatchFunc "abracadabra"

test :: IO ()
test
  = foldr1 (>>) [ putStrLn t | t <- tests ]
  where
	tests = [
		show (ex_abra "abracadabra"),
		show (ex_abra "ababracadabra"),
		show (ex_abra "ababracadabrabra"),
		show (ex_abra "ababrabracadabrabra")
		]