It's way too stupid to be considered a proper segmentation tool, but I'm hoping that what I upload will offend an NLP hacker enough for them to take over and submit something better.
To fetch the full source, including Cabal file, test suite runner, etc, just
darcs get --lazy http://patch-tag.com/r/kowey/fullstop-- ----------------------------------------------------------------------
module NLP.FullStop ( segment, testSuite ) where
import Data.List.Split -- lovely little library by Brent Yorgey
{- other imports elided -}
-- tests
testSuite :: Test.Framework.Test
testSuite =
testGroup "NLP.FullStop"
[ testGroup "basic sanity checking"
[ testProperty "concat (segment s) == id s, modulo whitespace" prop_segment_concat
]
, testGroup "segmentation"
[ testCaseSegments "simple" ["Foo.", "Bar."] "Foo. Bar."
, testCaseSegments "condense" ["What?!", "Yeah"] "What?! Yeah"
, testCaseSegments "numbers" ["Hello.", "3.14"] "Hello. 3.14"
, testCaseSegments "URLs" ["Check out http://www.example.com.", "OK?"]
"Check out http://www.example.com. OK?"
, testCaseSegments "titles" ["Mr. Doe, Mrs. Durand and Dr. Singh"]
"Mr. Doe, Mrs. Durand and Dr. Singh"
]
]
testCaseSegments d xs x = testCase d $ assertEqual "" xs (segment x)
prop_segment_concat s =
noWhite s == concatMap noWhite (segment s)
where
noWhite = filter (not . isSpace)
-- breaking things apart
segment :: String -> [String]
segment = map (dropWhile isSpace) . squish
. ( split
. condense -- "huh?!"
. dropFinalBlank -- strings that end with terminator
. keepDelimsR -- we want to preserve terminators
$ oneOf stopPunctuation
)
stopPunctuation = [ '.', '?', '!' ]
titles = [ "Mr.", "Mrs.", "Dr." ]
-- putting them back together
squish = squishBy (\_ y -> not (startsWithSpace y))
. squishBy (\x _ -> any (`isSuffixOf` x) titles)
. squishBy (\x y -> endsWithDigit x && startsWithDigit y)
where
startsW f [] = False
startsW f (x:_) = f x
--
startsWithDigit = startsW isDigit
startsWithSpace = startsW isSpace
--
endsWithDigit xs =
case reverse xs of
('.':x:_) -> isDigit x
_ -> False
squishBy f = map concat . groupBy f
