packages feed

polimorf 0.4.1 → 0.5.0

raw patch · 2 files changed

+135/−46 lines, 2 filesdep +dawgPVP ok

version bump matches the API change (PVP)

Dependencies added: dawg

API changes (from Hackage documentation)

+ Data.PoliMorf: Rule :: !Int -> !Text -> Rule
+ Data.PoliMorf: apply :: Rule -> Text -> Text
+ Data.PoliMorf: atomic :: Entry -> Bool
+ Data.PoliMorf: cut :: Rule -> !Int
+ Data.PoliMorf: data Rule
+ Data.PoliMorf: instance Eq Rule
+ Data.PoliMorf: instance Ord Rule
+ Data.PoliMorf: instance Show Rule
+ Data.PoliMorf: mergeWith :: Ord a => (String -> String -> a -> a) -> BaseMap -> DAWG (Set a) -> DAWG (Map a RelCode)
+ Data.PoliMorf: mkFormMap :: [Entry] -> FormMap
+ Data.PoliMorf: mkRuleMap :: [(Text, Text)] -> DAWG (Set Rule)
+ Data.PoliMorf: suffix :: Rule -> !Text
+ Data.PoliMorf: toBase :: Entry -> Maybe Rule
+ Data.PoliMorf: type FormMap = DAWG (Set Rule)
- Data.PoliMorf: merge :: Ord a => BaseMap -> Map Form (Set a) -> Map Form (Map a RelCode)
+ Data.PoliMorf: merge :: Ord a => BaseMap -> DAWG (Set a) -> DAWG (Map a RelCode)
- Data.PoliMorf: type BaseMap = Map Form (Set Base)
+ Data.PoliMorf: type BaseMap = DAWG (Set Rule)

Files

Data/PoliMorf.hs view
@@ -1,3 +1,4 @@+{-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE PatternGuards #-} {-# LANGUAGE BangPatterns #-} @@ -8,35 +9,45 @@  module Data.PoliMorf ( --- * Types+-- * Core types   Form , Base , Tag , Cat , Entry (..)+, atomic  -- * Parsing , readPoliMorf , parsePoliMorf  -- * Merging+, Rule (..)+, apply+, toBase+, mkRuleMap , BaseMap , mkBaseMap+, FormMap+, mkFormMap , RelCode (..)+, mergeWith , merge ) where  import Control.Applicative ((<$>), (<*>)) import Data.Maybe (catMaybes) import Data.Monoid (mappend)-import Data.List (foldl') import Data.Binary (Binary, get, put)-import qualified Data.Map as M import qualified Data.Set as S+import qualified Data.Map as M import qualified Data.Text as T import qualified Data.Text.Lazy as L import qualified Data.Text.Lazy.IO as L+import qualified Data.DAWG as D +import Debug.Trace (trace)+ -- | A form. type Form = T.Text @@ -57,6 +68,15 @@     , cat  :: !Cat }     deriving (Eq, Ord, Show, Read) +-- | Is the entry an atomic one?  More precisely, we treat all negative+-- forms starting with ''nie'' and all superlatives starting with ''naj''+-- as non-atomic entries.+atomic :: Entry -> Bool+atomic x+    | "sup" `T.isInfixOf` tag x && "naj" `T.isPrefixOf` form x = False+    | "neg" `T.isInfixOf` tag x && "nie" `T.isPrefixOf` form x = False+    | otherwise = True+ -- | Read the PoliMorf from the file. readPoliMorf :: FilePath -> IO [Entry] readPoliMorf path = parsePoliMorf <$> L.readFile path@@ -71,25 +91,74 @@     [_form, _base, _tag, _cat] -> Entry _form _base _tag _cat     _   -> error $ "parsePoliRow: invalid row \"" ++ L.unpack row ++ "\"" +-- | A rule for translating a form into another one.+data Rule = Rule {+    -- | Number of characters to cut from the end of the form.+      cut       :: !Int+    -- | A suffix to paste.+    , suffix    :: !T.Text }+    deriving (Show, Eq, Ord)++-- | Apply the rule.+apply :: Rule -> T.Text -> T.Text+apply r x = T.take (T.length x - cut r) x `T.append` suffix r++-- | Determine the rule needed to translate the form into its base form.+toBase :: Entry -> Maybe Rule+toBase x+    | "sup" `T.isInfixOf` tag x && "naj" `T.isPrefixOf` form x = Nothing+    | "neg" `T.isInfixOf` tag x && "nie" `T.isPrefixOf` form x = Nothing+    | otherwise =+        let k = lcp (form x) (base x)+        in  Just $ Rule (T.length (form x) - k) (T.drop k (base x))+  where+    lcp a b = case T.commonPrefixes a b of+        Just (c, _, _)  -> T.length c+        Nothing         -> trace (show (form x, base x)) 0++-- | Make a rule to translate between two strings.+between :: T.Text -> T.Text -> Rule+between source dest =+    let k = lcp source dest+    in  Rule (T.length source - k) (T.drop k dest)+  where+    lcp a b = case T.commonPrefixes a b of+        Just (c, _, _)  -> T.length c+        Nothing         -> 0+ -- | A map from forms to their possible base forms (there may be many since -- the form may be a member of multiple lexemes).-type BaseMap = M.Map Form (S.Set Base)+type BaseMap = D.DAWG (S.Set Rule) --- | Make the base map from the list of entries.+-- | A map from base forms to all their potential forms.+type FormMap = D.DAWG (S.Set Rule)++-- | Make a rule map from a list of entries.+mkRuleMap :: [(T.Text, T.Text)] -> D.DAWG (S.Set Rule)+mkRuleMap xs = D.fromListWith S.union $+    [ ( T.unpack x+      , S.singleton (between x y) )+    | (x, y) <- xs ]++-- | Make a 'BaseMap' from a list of entries. mkBaseMap :: [Entry] -> BaseMap-mkBaseMap = M.fromListWith S.union . map ((,) <$> form <*> S.singleton . base)+mkBaseMap = mkRuleMap . map ((,) <$> form <*> base) +-- | Make a 'FormMap' from a list of entries.+mkFormMap :: [Entry] -> FormMap+mkFormMap = mkRuleMap . map ((,) <$> base <*> form)+ -- | Reliability information: how did we assign a particular label to -- a particular word form. data RelCode-    = Exact     -- ^ Label assigned in a direct manner+    = ByForm    -- ^ Based on labels of other forms within the same lexeme     | ByBase    -- ^ Label assigned based on a lemma label  -    | ByForm    -- ^ Based on labels of other forms within the same lexeme+    | Exact     -- ^ Label assigned in a direct manner     deriving (Eq, Ord, Show, Read)  instance Binary RelCode where     put Exact   = put '1'-    put ByBase = put '2'+    put ByBase  = put '2'     put ByForm  = put '3'     get = get >>= \x -> return $ case x of         '1' -> Exact@@ -97,63 +166,82 @@         '3' -> ByForm         c   -> error $ "get: invalid RelCode code '" ++ [c] ++ "'" --- | Merge the 'BaseMap' with the dictionary resource which maps forms to--- sets of labels.  Every label is assigned a 'RelCode' which tells what--- is the relation between the label and the form.  There are three--- kinds of labels:+-- | Merge the 'BaseMap' with the dictionary resource which maps forms to sets+-- of labels.  Every label is assigned a 'RelCode' which tells what is the+-- relation between the label and the form. It is a generalized version+-- of the 'merge' function with additional function @f x y y'label@ which+-- can be used to determine the resultant set of labels for the form @x@+-- given ,,similar'' form @y@ and its original label @y'label@.+-- There are three kinds of labels: -- 'Exact' labels assigned in a direct manner, 'ByBase' labels assigned -- to all forms which have a base form with a label in the input dictionary, -- and 'ByForm' labels assigned to all forms which have a related form from the -- same lexeme with a label in the input dictionary.------ This function is far from being memory efficient right now.  If you plan to--- run it with respect to the entire PoliMorf dictionary you should do it--- on a machine with an abundance of available memory.-merge-    :: Ord a => BaseMap-    -> M.Map Form (S.Set a)-    -> M.Map Form (M.Map a RelCode)-merge poli dict0 = M.fromList+mergeWith+    :: Ord a+    => (String -> String -> a -> a)+    -> BaseMap+    -> D.DAWG (S.Set a)+    -> D.DAWG (M.Map a RelCode)+mergeWith f poli dict0 = D.fromList     [ (x, combine x)     | x <- keys ]   where     -- Keys in the output dictionary.-    keys = S.toList (M.keysSet poli `S.union` M.keysSet dict0)+    keys = join (D.keys poli) (D.keys dict0)      -- Combining function.-    combine x = (M.unionsWith min . catMaybes)-        [ label Exact  <$> M.lookup x dict0 -        , label ByBase <$> M.lookup x dict1-        , label ByForm <$> M.lookup x dict2 ]+    combine x = (M.unionsWith max . catMaybes)+        [ label Exact  <$> D.lookup x dict0 +        , label ByBase <$> D.lookup x dict1+        , label ByForm <$> D.lookup x dict2 ]      label :: Ord a => RelCode -> S.Set a -> M.Map a RelCode     label code s = M.fromList [(x, code) | x <- S.toList s]      -- Extended to all base forms of dict0 keys.-    dict1 = fromListWith mappend-        [ (lemma, x)-        | (_form, x) <- M.assocs dict0+    dict1 = D.fromListWith mappend+        [ (lemma, f'Set lemma _form x)+        | (_form, x) <- D.assocs dict0         , lemma <- elemsOn poli _form ]      -- Extended to all forms of dict0 keys.-    dict2 = fromListWith mappend-        [ (form', x)-        | (_form, x) <- M.assocs dict0+    dict2 = D.fromListWith mappend+        [ (form', f'Set form' _form x)+        | (_form, x) <- D.assocs dict0         , lemma <- elemsOn poli _form         , form' <- elemsOn ilop lemma ]      -- Inverse poli dictionary.-    ilop = fromListWith mappend-        [ (lemma, S.singleton _form)-        | (_form, lemmas) <- M.assocs poli-        , lemma <- S.toList lemmas ]+    ilop = mkRuleMap+        [ (base'Text, form'Text)+        | (form'String, rules) <- D.assocs poli+        , rule <- S.toList rules+        , let form'Text = T.pack form'String+        , let base'Text = apply rule form'Text ]+    +    -- Merge to ascending lists.+    join (x:xs) (y:ys)+        | x < y     = x : join xs (y:ys)+        | x > y     = y : join (x:xs) ys+        | otherwise = x : join xs ys+    join xs []  = xs+    join [] ys  = ys -elemsOn :: (Ord a, Ord b) => M.Map a (S.Set b) -> a -> [b]-elemsOn m x = case x `M.lookup` m of-    Just s  -> S.toList s-    Nothing -> []+    -- Version of f function working on label sets.+    f'Set v w = S.fromList . map (f v w) . S.toList -fromListWith :: Ord k => (a -> a -> a) -> [(k, a)] -> M.Map k a-fromListWith f xs =-    let update m (!k, !x) = M.insertWith' f k x m-    in  foldl' update M.empty xs+-- | A specialized version of the 'mergeWith' function which doesn't+-- change labels in the resultant 'D.DAWG'.+merge+    :: Ord a => BaseMap+    -> D.DAWG (S.Set a)+    -> D.DAWG (M.Map a RelCode)+merge = mergeWith $ \_ _ x -> x++elemsOn :: D.DAWG (S.Set Rule) -> String -> [String]+elemsOn m x = case x `D.lookup` m of+    Just s  ->+        [ T.unpack . apply rule . T.pack $ x+        | rule <- S.toList s ]+    Nothing -> []
polimorf.cabal view
@@ -1,5 +1,5 @@ name:               polimorf-version:            0.4.1+version:            0.5.0 synopsis:           Working with the PoliMorf dictionary description:     The library provides functionality for manipulating PoliMorf, the@@ -23,6 +23,7 @@       , containers       , text       , binary+      , dawg >= 0.5 && < 0.6      exposed-modules:         Data.PoliMorf