packages feed

morfette-0.2: GramLab/Data/CommonSubstrings.hs

{-# LANGUAGE NoMonomorphismRestriction#-} 
module GramLab.Data.CommonSubstrings ( lcs
                                     , lcsFiltering
                                     , commonSubstrings
                                     , substrings
                                     , toMap
                                     , CommonSubstring
                                     , Indices
                                     )

where

import Data.List
import qualified Data.Map as Map
import Data.Ord
import Data.Char
import qualified GramLab.Data.StringLike as S


type Indices = ([Int],[Int])
commonSubstrings :: (Ord s, Ord a, S.StringLike s a) => s -> s -> Map.Map s Indices
commonSubstrings xs ys = Map.intersectionWith (,) ((toMap . substrings) xs)
                                                  ((toMap . substrings) ys)

commonSubstringsWith equiv xs ys = [ (x,y,i_x,i_y) | (x,i_x) <- (Map.toList . toMap . substrings) xs
                                                   , (y,i_y) <- (Map.toList . toMap . substrings) ys
                                                   , x `equiv` y ]
collapseVowels []  = []
collapseVowels [x] = [x]
collapseVowels (x:y:xs) | vowel x && vowel y = '@':collapseVowels xs
                        | vowel x            = '@':collapseVowels (y:xs)
                        | otherwise          =  x :collapseVowels (y:xs)
   where vowel x = x `elem` "ieaou"

equiv xs ys = collapseVowels xs == collapseVowels ys

type  CommonSubstring s = (s,Indices)

longest :: (Ord s, Ord a, S.StringLike s a) => Map.Map s Indices -> [CommonSubstring s]
longest = sortBy (flip (comparing (S.length . fst))) . Map.toList

lcs :: (Ord a, Ord s, S.StringLike s a, Monad m) =>
       s -> s -> m (CommonSubstring s)
lcs = lcsFiltering (const True)
lcsFiltering :: (Ord s, Ord a, S.StringLike s a, Monad m) => 
                (CommonSubstring s -> Bool) 
             -> s 
             -> s 
             -> m (CommonSubstring s)
lcsFiltering f xs ys = case longest (Map.filterWithKey (curry f) (commonSubstrings xs ys)) of
                         [] -> fail "lcsFiltering: no common strings"
                         (x:_) -> return x

substrings :: (Enum b, Num b,Ord a, S.StringLike s a) => s -> [(s, b)]
substrings xs = [ (S.fromString $ map fst suffix, snd . head $ suffix) 
                      | prefix <- tail $ inits' (zip (S.toString xs) [0..])
                      , suffix <- init $ tails prefix ]
inits' = reverse . map reverse . tails . reverse

toMap :: (Ord k) => [(k,v)] -> Map.Map k [v]
toMap = foldr (\(k,v) z -> Map.insertWith (++) k [v] z) Map.empty