sentence-jp-0.1.0.0: Control/SentenceJP.hs
{-# LANGUAGE OverloadedStrings #-}
module Control.SentenceJP
( generateSentence
) where
import Control.Monad.IO.Class ( MonadIO (), liftIO )
import Data.List ( concat, foldl1 )
import Data.Text ( Text () )
import Prelude hiding ( foldl1 )
import System.Random.Shuffle ( shuffleM )
import Text.MeCab ( new, parseToNodes, Node (..) )
import qualified Data.Text as T
--- Define data types
--
data PositionText = Begin Text | Middle Text | End Text deriving (Show)
extractPositionText :: PositionText -> Text
extractPositionText (Begin x) = x
extractPositionText (Middle x) = x
extractPositionText (End x) = x
isEnd :: PositionText -> Bool
isEnd (End _) = True
isEnd _ = False
type PositionTextCons = (Text -> PositionText)
getPosition :: PositionText -> PositionTextCons
getPosition (Begin _) = Begin
getPosition (Middle _) = Middle
getPosition (End _) = End
data ChainableWords = ChainableWords PositionText PositionText deriving (Show)
areChainable :: ChainableWords -> ChainableWords -> Bool
areChainable (ChainableWords _ y1) (ChainableWords x2 _) =
let jointL = extractPositionText y1
jointR = extractPositionText x2
in jointL == jointR
toText :: ChainableWords -> Text
toText (ChainableWords x y) = extractPositionText x `T.append` extractPositionText y
hasBegin :: ChainableWords -> Bool
hasBegin (ChainableWords (Begin _) _) = True
hasBegin _ = False
--- Define functions
--
-- | Generating Sentence as Text from [Text]
generateSentence :: MonadIO m => [Text] -> m Text
generateSentence sources = do
mecab <- liftIO . new $ ["mecab"]
nodes <- liftIO . mapM (parseToNodes mecab) $ sources
let sentences = map (toChainable . filter (/= "") . map nodeSurface) $ nodes
sentences' <- liftIO . shuffleM . concat $ sentences
return . chainWords $ sentences'
toChainable :: [Text] -> [ChainableWords]
toChainable [x] = [ChainableWords (Begin x) (End "")]
toChainable xs = zipWith ChainableWords xs' (tail xs')
where
beginWord = Begin . head $ xs
endWord = End . last $ xs
middleWords = map Middle . init . tail $ xs
xs' = [beginWord] ++ middleWords ++ [endWord]
chainWords :: [ChainableWords] -> Text
chainWords [] = error "aho-baka-hoge"
chainWords [x] = toText x
chainWords xs = toText . foldl1 chain . dropWhile (not . hasBegin) $ xs
where
chain :: ChainableWords -> ChainableWords -> ChainableWords
chain a@(ChainableWords _ y1) b@(ChainableWords _ y2)
| areChainable a b = if isEnd y1
then a
else let l = Begin . toText $ a
in ChainableWords l y2
| otherwise = a