hist-pl-fusion 0.3.0 → 0.4.0
raw patch · 3 files changed
+44/−287 lines, 3 filesdep −cmdargsdep ~hist-pl-lexiconPVP ok
version bump matches the API change (PVP)
Dependencies removed: cmdargs
Dependency ranges changed: hist-pl-lexicon
API changes (from Hackage documentation)
- NLP.HistPL.Fusion: Copy :: Code
- NLP.HistPL.Fusion: Lex :: LexKey i -> LexElem a b -> Lex i a b
- NLP.HistPL.Fusion: LexElem :: a -> Map Word b -> LexElem a b
- NLP.HistPL.Fusion: LexKey :: Text -> i -> LexKey i
- NLP.HistPL.Fusion: Orig :: Code
- NLP.HistPL.Fusion: Rule :: !Int -> !Text -> Rule
- NLP.HistPL.Fusion: apply :: Rule -> Text -> Text
- NLP.HistPL.Fusion: between :: Text -> Text -> Rule
- NLP.HistPL.Fusion: cut :: Rule -> !Int
- NLP.HistPL.Fusion: data Code
- NLP.HistPL.Fusion: data Lex i a b
- NLP.HistPL.Fusion: data LexElem a b
- NLP.HistPL.Fusion: data LexKey i
- NLP.HistPL.Fusion: data Rule
- NLP.HistPL.Fusion: entries :: Ord i => Dict i a b -> [Lex i a b]
- NLP.HistPL.Fusion: extend :: HLex -> PLexSet -> FLex
- NLP.HistPL.Fusion: forms :: LexElem a b -> Map Word b
- NLP.HistPL.Fusion: fuse :: Corresp -> Hist -> Poli -> Fused
- NLP.HistPL.Fusion: info :: LexElem a b -> a
- NLP.HistPL.Fusion: instance (Eq a, Eq b) => Eq (LexElem a b)
- NLP.HistPL.Fusion: instance (Eq i, Eq a, Eq b) => Eq (Lex i a b)
- NLP.HistPL.Fusion: instance (Ord a, Ord b) => Ord (LexElem a b)
- NLP.HistPL.Fusion: instance (Ord i, Binary i, Binary a, Binary b) => Binary (Bila i a b)
- NLP.HistPL.Fusion: instance (Ord i, Ord a, Ord b) => Ord (Lex i a b)
- NLP.HistPL.Fusion: instance (Show a, Show b) => Show (LexElem a b)
- NLP.HistPL.Fusion: instance (Show i, Show a, Show b) => Show (Lex i a b)
- NLP.HistPL.Fusion: instance Binary Code
- NLP.HistPL.Fusion: instance Binary Rule
- NLP.HistPL.Fusion: instance Eq Code
- NLP.HistPL.Fusion: instance Eq Rule
- NLP.HistPL.Fusion: instance Eq i => Eq (LexKey i)
- NLP.HistPL.Fusion: instance Ord Code
- NLP.HistPL.Fusion: instance Ord Rule
- NLP.HistPL.Fusion: instance Ord i => Ord (LexKey i)
- NLP.HistPL.Fusion: instance Show Code
- NLP.HistPL.Fusion: instance Show Rule
- NLP.HistPL.Fusion: instance Show i => Show (LexKey i)
- NLP.HistPL.Fusion: key :: LexKey i -> Text
- NLP.HistPL.Fusion: lexElem :: Lex i a b -> LexElem a b
- NLP.HistPL.Fusion: lexKey :: Lex i a b -> LexKey i
- NLP.HistPL.Fusion: lookup :: Ord i => Text -> Dict i a b -> LexSet i a b
- NLP.HistPL.Fusion: mkDict :: (Ord i, Ord a, Ord b) => [(Text, i, a, Text, b)] -> Dict i a b
- NLP.HistPL.Fusion: mkHist :: [BinEntry] -> Hist
- NLP.HistPL.Fusion: mkLexSet :: Ord i => [Lex i a b] -> LexSet i a b
- NLP.HistPL.Fusion: revDict :: (Ord i, Ord a, Ord b) => Dict i a b -> Dict i a b
- NLP.HistPL.Fusion: suffix :: Rule -> !Text
- NLP.HistPL.Fusion: type BaseDict i a b = Dict i a b
- NLP.HistPL.Fusion: type Dict i a b = DAWG Char () (Map i (a, Map Rule b))
- NLP.HistPL.Fusion: type FLex = Lex UID () Code
- NLP.HistPL.Fusion: type FormDict i a b = Dict i a b
- NLP.HistPL.Fusion: type Fused = BaseDict UID () Code
- NLP.HistPL.Fusion: type HLex = Lex UID (Set POS) IsBase
- NLP.HistPL.Fusion: type Hist = BaseDict UID (Set POS) IsBase
- NLP.HistPL.Fusion: type LexSet i a b = Map (LexKey i) (LexElem a b)
- NLP.HistPL.Fusion: uid :: LexKey i -> i
- NLP.HistPL.Fusion: unDict :: (Ord i, Ord a, Ord b) => Dict i a b -> [(Text, i, a, Text, b)]
- NLP.HistPL.Fusion: unLexSet :: LexSet i a b -> [Lex i a b]
- NLP.HistPL.Fusion: type Core = Poli -> HLex -> [PLexSet]
+ NLP.HistPL.Fusion: type Core = Poli -> LexEntry -> [PLexSet]
- NLP.HistPL.Fusion: type Corresp = Poli -> HLex -> PLexSet
+ NLP.HistPL.Fusion: type Corresp = Poli -> LexEntry -> PLexSet
- NLP.HistPL.Fusion: type Filter = HLex -> PLex -> Bool
+ NLP.HistPL.Fusion: type Filter = LexEntry -> PLex -> Bool
Files
- hist-pl-fusion.cabal +9/−9
- src/NLP/HistPL/Fusion.hs +35/−240
- tools/hist-pl-fuse.hs +0/−38
hist-pl-fusion.cabal view
@@ -1,5 +1,5 @@ name: hist-pl-fusion-version: 0.3.0+version: 0.4.0 synopsis: Merging historical dictionary with PoliMorf description: The library provides functions for merging historical dictionary@@ -25,7 +25,7 @@ , text-binary , dawg >= 0.9 && < 0.10 , polimorf >= 0.7.1 && < 0.8- , hist-pl-lexicon >= 0.3 && < 0.4+ , hist-pl-lexicon >= 0.4 && < 0.5 exposed-modules: NLP.HistPL.Fusion@@ -36,10 +36,10 @@ type: git location: https://github.com/kawu/hist-pl.git -executable hist-pl-fuse- build-depends:- binary- , cmdargs- hs-source-dirs: src, tools- main-is: hist-pl-fuse.hs- ghc-options: -Wall -O2+-- executable hist-pl-fuse+-- build-depends:+-- binary+-- , cmdargs+-- hs-source-dirs: src, tools+-- main-is: hist-pl-fuse.hs+-- ghc-options: -Wall -O2
src/NLP/HistPL/Fusion.hs view
@@ -3,43 +3,18 @@ module NLP.HistPL.Fusion (--- * Rule- Rule (..)-, apply-, between- -- * Basic types-, UID+ UID , POS , Word , Base , IsBase -- * Dictionary--- ** Entry-, Lex (..)-, LexKey (..)-, LexElem (..)-, LexSet-, mkLexSet-, unLexSet--- ** Dictionary-, Dict-, BaseDict-, FormDict-, mkDict-, unDict-, revDict-, lookup-, entries -- ** Bilateral , Bila (..) , mkBila , withForm--- ** Historical-, Hist-, mkHist-, HLex -- ** Contemporary , Poli , PLex@@ -56,185 +31,60 @@ , byForms , posFilter , sumChoice---- * Fusion-, Fused-, FLex-, Code (..)-, extend-, fuse ) where import Prelude hiding (lookup) import Control.Applicative ((<$>), (<*>))-import Control.Arrow (first)-import Data.Binary (Binary, get, put) import Data.Text.Binary ()-import qualified Data.Set as S import qualified Data.Map as M import qualified Data.Text as T import qualified Data.PoliMorf as P-import qualified Data.DAWG.Static as D -import qualified NLP.HistPL as H---- | A rule for translating a form into another form.-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)--instance Binary Rule where- put Rule{..} = put cut >> put suffix- get = Rule <$> get <*> get---- | Apply the rule.-apply :: Rule -> T.Text -> T.Text-apply r x = T.take (T.length x - cut r) x `T.append` suffix r---- | 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+import NLP.HistPL.Lexicon (UID)+import qualified NLP.HistPL.Lexicon as H+import qualified NLP.HistPL.Util as H+import NLP.HistPL.Dict ------------------------------------------------------------------------ --- | Unique ID in historical dictionary.-type UID = Int -- | Part of speech. type POS = T.Text + -- | Base form. type Base = T.Text + -- | Word form. type Word = T.Text + -- | Is the word form a base form? type IsBase = Bool ------------------------------------------------------------------------- --- | A lexical entry.-data Lex i a b = Lex- { lexKey :: LexKey i- , lexElem :: LexElem a b }- deriving (Show, Eq, Ord)---- | Transform entry into a list.-listLex :: Lex i a b -> [(T.Text, i, a, T.Text, b)]-listLex Lex{..} =- [ (key, uid, info, word, y)- | (word, y) <- M.assocs forms ]- where- LexKey{..} = lexKey- LexElem{..} = lexElem---- | Lexical entry dictionary key.-data LexKey i = LexKey- { key :: T.Text- , uid :: i }- deriving (Show, Eq, Ord)---- | Lexical entry info.-data LexElem a b = LexElem- { info :: a- , forms :: M.Map Word b }- deriving (Show, Eq, Ord)---- | A set of lexical entries in a map form.-type LexSet i a b = M.Map (LexKey i) (LexElem a b) ---- | Make lexical set from a list of entries.-mkLexSet :: Ord i => [Lex i a b] -> LexSet i a b-mkLexSet = M.fromList . map ((,) <$> lexKey <*> lexElem)---- | List lexical entries.-unLexSet :: LexSet i a b -> [Lex i a b]-unLexSet = map (uncurry Lex) . M.toList----------------------------------------------------------------------------type RuleEntry i a b = M.Map i (a, M.Map Rule b)---- | One-way dictionary parametrized over ID @i@, with info @a@ for every--- (key, i) pair and info @b@ for every (key, i, apply rule key) triple.-type Dict i a b = D.DAWG Char () (M.Map i (a, M.Map Rule b))- -- | Dictionary keys represent base forms and rules transform base forms to -- their corresponding word forms. Info @a@ is assigned to every lexeme -- and info @b@ to every word form. type BaseDict i a b = Dict i a b + -- | Dictionary keys represent word forms and rules transform word forms to -- their corresponding base forms. Info @a@ is assigned to every lexeme -- and info @b@ to every word form. type FormDict i a b = Dict i a b --- | Decode dictionary entry.-decode :: Ord i => T.Text -> RuleEntry i a b -> LexSet i a b-decode key ruleEntry = mkLexSet- [ Lex- (LexKey key i)- (LexElem x $ M.fromList- [ (apply rule key, y)- | (rule, y) <- M.assocs ruleMap ])- | (i, (x, ruleMap)) <- M.assocs ruleEntry ] --- | Lookup the key in the dictionary.-lookup :: Ord i => T.Text -> Dict i a b -> LexSet i a b-lookup key dict = decode key $ case D.lookup (T.unpack key) dict of- Just m -> m- Nothing -> M.empty---- | List dictionary lexical entries.-entries :: Ord i => Dict i a b -> [Lex i a b]-entries =- let f = unLexSet . uncurry decode . first T.pack - in concatMap f . D.assocs---- | Make dictionary from a list of (key, ID, key\/ID info, elem,--- key\/ID\/elem info) tuples.-mkDict :: (Ord i, Ord a, Ord b) => [(T.Text, i, a, T.Text, b)] -> Dict i a b-mkDict xs = D.fromListWith union $- [ ( T.unpack x- , M.singleton i- (a, M.singleton (between x y) b) )- | (x, i, a, y, b) <- xs ]- where- union = M.unionWith $ both const M.union- both f g (x, y) (x', y') = (f x x', g y y')---- | Transform dictionary back into the list of (key, ID, key\/ID info, elem,--- key\/ID\/elem info) tuples.-unDict :: (Ord i, Ord a, Ord b) => Dict i a b -> [(T.Text, i, a, T.Text, b)]-unDict = concatMap listLex . entries---- | Reverse the dictionary.-revDict :: (Ord i, Ord a, Ord b) => Dict i a b -> Dict i a b-revDict = - let swap (base, i, x, form, y) = (form, i, x, base, y)- in mkDict . map swap . unDict- ------------------------------------------------------------------------ + -- | Bilateral dictionary. data Bila i a b = Bila { baseDict :: BaseDict i a b , formDict :: FormDict i a b } deriving (Show, Eq, Ord) -instance (Ord i, Binary i, Binary a, Binary b) => Binary (Bila i a b) where- put Bila{..} = put baseDict >> put formDict- get = Bila <$> get <*> get -- | Make bilateral dictionary from a list of (base form, ID, additional -- lexeme info, word form, additional word form info) tuples.@@ -243,65 +93,47 @@ { baseDict = baseDict' , formDict = formDict' } where- baseDict' = mkDict xs+ baseDict' = fromList xs formDict' = revDict baseDict' + -- | Identify entries which contain given word form. withForm :: Ord i => Bila i a b -> Word -> LexSet i a b withForm Bila{..} word = M.unions [ lookup base baseDict- | (_, lexElem) <- M.assocs (lookup word formDict)- , base <- M.keys (forms lexElem) ]+ | let lexSet = lookup word formDict+ , (_, val) <- M.assocs lexSet+ , base <- M.keys (forms val) ] + ------------------------------------------------------------------------ + -- | PoliMorf dictionary in a bilateral form. type Poli = Bila POS () () + -- | PoliMorf dictionary entry. type PLex = Lex POS () () + -- | Set of PoliMorf dictionary entries. type PLexSet = LexSet POS () () + -- | Make bilateral dictionary from PoliMorf. mkPoli :: [P.Entry] -> Poli mkPoli = mkBila . map ((,,(),,()) <$> P.base <*> P.pos <*> P.form) ------------------------------------------------------------------------- --- | Historical dictionary.-type Hist = BaseDict UID (S.Set POS) IsBase---- | Historical dictionary entry.-type HLex = Lex UID (S.Set POS) IsBase---- | Construct historical dictionary.-mkHist :: [H.BinEntry] -> Hist-mkHist xs = mkDict- [ ( H.keyForm key- , H.keyUid key- , S.fromList (H.pos entry)- , form- , isBase )- | binEntry <- xs- , let key = H.binKey binEntry- , let entry = H.lexEntry binEntry- , (form, isBase) <-- map (,True) (lemmas entry) ++- map (,False) (forms entry)- , oneWord form ]- where- lemmas = H.text . H.lemma- forms = concatMap H.text . H.forms- oneWord = (==1) . length . T.words- ------------------------------------------------------------------------ + -- | A function which determines entries from a bilateral -- dictionary corresponing to a given historical lexeme.-type Corresp = Poli -> HLex -> PLexSet+type Corresp = Poli -> H.LexEntry -> PLexSet + -- | We provide three component types, `Core`, `Filter` and `Choice`, which -- can be combined together using the `buildCorresp` function to construct -- a `Corresp` function. The first one, `Core`, is used to identify a list@@ -310,78 +142,41 @@ -- be usually divided into a set of smaller tasks of the same purpose. -- For example, we may want to identify `LexSet`s corresponding to individual -- word forms of the historical lexeme.-type Core = Poli -> HLex -> [PLexSet]+type Core = Poli -> H.LexEntry -> [PLexSet] + -- | Function which can be used to filter out lexemes which do not -- satisfy a particular predicate. For example, we may want to filter -- out lexemes with incompatible POS value.-type Filter = HLex -> PLex -> Bool+type Filter = H.LexEntry -> PLex -> Bool + -- | The final choice of lexemes. Many different strategies can be used--- here -- sum of the sets, intersection, or voting.+-- here sum of the sets, intersection, or voting. type Choice = [PLexSet] -> PLexSet + -- | Identify `LexSet`s corresponding to individual word forms of the -- historical lexeme using the `withForm` function. byForms :: Core-byForms bila Lex{..} =+byForms bila lexEntry = [ withForm bila word- | word <- M.keys (forms lexElem) ]+ | word <- H.allForms lexEntry ] + -- | Filter out lexemes with POS value incompatible with the -- set of POS values assigned to the historical lexeme. posFilter :: Filter-posFilter h p = uid (lexKey p) `S.member` info (lexElem h)+posFilter h p = uid (lexKey p) `elem` H.pos h + -- | Sum of sets of lexemes. sumChoice :: Choice sumChoice = M.unions + -- | Build `Corresp` function form individual components. buildCorresp :: Core -> Filter -> Choice -> Corresp buildCorresp core filt choice bila hLex = let filterSet = mkLexSet . filter (filt hLex) . unLexSet in choice . map filterSet . core bila $ hLex------------------------------------------------------------------------------ | Fused dictionary.-type Fused = BaseDict UID () Code---- | Fused dictionary entry.-type FLex = Lex UID () Code---- | Code of word form origin.-data Code- = Orig -- ^ original (was already present in `HLex`)- | Copy -- ^ a copy (from corresponding lexeme)- deriving (Show, Eq, Ord)--instance Binary Code where- put Orig = put '1'- put Copy = put '2'- get = get >>= \x -> return $ case x of- '1' -> Orig- '2' -> Copy- c -> error $ "get: invalid Code value '" ++ [c] ++ "'"---- | Extend lexeme with forms from the set of lexemes.-extend :: HLex -> PLexSet -> FLex-extend hLex lexSet = subForms . M.fromList $- concatMap (fromElem Copy) (M.elems lexSet) ++- fromElem Orig (lexElem hLex)- where- subForms x = hLex { lexElem = LexElem () x }- fromElem code = map (,code) . (M.keys . forms)---- | Fuse the historical dictionary with bilateral contemporary--- dictionary using the given `Corresp` function to determine--- contemporary lexemes corresponding to individual lexemes--- from the historical dictionary.-fuse :: Corresp -> Hist -> Poli -> Fused-fuse corr hist bila = mkDict- [ (key, uid, (), word, code)- | hLex <- entries hist- , let Lex{..} = extend hLex (corr bila hLex)- , let LexKey{..} = lexKey- , (word, code) <- M.assocs (forms lexElem) ]
− tools/hist-pl-fuse.hs
@@ -1,38 +0,0 @@-{-# LANGUAGE DeriveDataTypeable #-}-{-# LANGUAGE OverloadedStrings #-}-{-# LANGUAGE ScopedTypeVariables #-}-{-# LANGUAGE RecordWildCards #-}--import Control.Applicative ((<$>))-import System.Console.CmdArgs-import Data.Binary (encodeFile)--import qualified Data.PoliMorf as P-import qualified NLP.HistPL as H-import qualified NLP.HistPL.Fusion as F--data HistPL_Fuse = HistPL_Fuse- { histPath :: FilePath- , poliPath :: FilePath- , outPath :: FilePath }- deriving (Data, Typeable, Show)--histFuse :: HistPL_Fuse-histFuse = HistPL_Fuse- { histPath = def &= typ "HistPL-Binary" &= argPos 0- , poliPath = def &= typ "PoliMorf" &= argPos 1- , outPath = def &= typ "Output-Analysis-DAWG" &= argPos 2 }--main :: IO ()-main = exec =<< cmdArgs histFuse--exec :: HistPL_Fuse -> IO ()-exec HistPL_Fuse{..} = do- poli <- F.mkPoli . filter P.atomic <$> P.readPoliMorf poliPath- hist <- H.load histPath >>= \x -> case x of- Nothing -> error "hist-pl-fuse: not a binary historical dictionary"- Just xs -> return $ F.mkHist xs- let dict = F.fuse corr hist poli- encodeFile outPath (F.revDict dict)- where- corr = F.buildCorresp F.byForms F.posFilter F.sumChoice