From 6c9912fa2933444de23f7de13a73257af5bf7e73 Mon Sep 17 00:00:00 2001 From: Joshua Clayton Date: Tue, 17 May 2016 23:15:27 -0400 Subject: [PATCH] Switch to faster/more naive implementation of String to Int conversion Why? ==== This library converts lots of strings to positive integers (specifically, when it's parsing output from search results). Because these are always positive integers, we can make more assumptions about the data and how to parse the values. Corresponding benchmark: https://gist.github.com/joshuaclayton/767c507edf09215d08cdd79c93a5f383 --- src/Unused/Parser/Internal.hs | 7 ++----- src/Unused/Util.hs | 9 +++++++++ test/Unused/UtilSpec.hs | 9 ++++++++- 3 files changed, 19 insertions(+), 6 deletions(-) diff --git a/src/Unused/Parser/Internal.hs b/src/Unused/Parser/Internal.hs index cf5effb..5bc6c6e 100644 --- a/src/Unused/Parser/Internal.hs +++ b/src/Unused/Parser/Internal.hs @@ -7,10 +7,10 @@ module Unused.Parser.Internal import Control.Monad (void) import Data.Maybe (fromMaybe) -import Text.Read (readMaybe) import Text.Parsec import Text.Parsec.String (Parser) import Unused.Types (TermMatch(..)) +import Unused.Util (stringToInt) parseTermMatches :: Parser [TermMatch] parseTermMatches = many1 parseTermMatch <* eof @@ -37,10 +37,7 @@ pathParser = many1 (noneOf ":") occurrenceParser :: Parser Int occurrenceParser = - toInt <$> many1 digit - where - toInt = fromMaybe 0 . maybeInt - maybeInt s = readMaybe s :: Maybe Int + fromMaybe 0 . stringToInt <$> many1 digit eol :: Parser String eol = try (string "\n\r") diff --git a/src/Unused/Util.hs b/src/Unused/Util.hs index 27f5ae8..ac1894c 100644 --- a/src/Unused/Util.hs +++ b/src/Unused/Util.hs @@ -1,12 +1,21 @@ module Unused.Util ( groupBy + , stringToInt ) where import Control.Arrow ((&&&)) import qualified Data.List as L import Data.Function +import Data.Char (digitToInt, isDigit) groupBy :: (Ord b) => (a -> b) -> [a] -> [(b, [a])] groupBy f = map (f . head &&& id) . L.groupBy ((==) `on` f) . L.sortBy (compare `on` f) + +stringToInt :: String -> Maybe Int +stringToInt xs + | all isDigit xs = Just $ loop 0 xs + | otherwise = Nothing + where + loop = foldl (\acc x -> acc * 10 + digitToInt x) diff --git a/test/Unused/UtilSpec.hs b/test/Unused/UtilSpec.hs index f4f3fe0..7a112ad 100644 --- a/test/Unused/UtilSpec.hs +++ b/test/Unused/UtilSpec.hs @@ -15,7 +15,7 @@ main :: IO () main = hspec spec spec :: Spec -spec = parallel $ +spec = parallel $ do describe "groupBy" $ do it "groups by the result of a function" $ do let numbers = [1..10] :: [Int] @@ -27,3 +27,10 @@ spec = parallel $ groupBy pName people `shouldBe` [("Jane", [Person "Jane" 10, Person "Jane" 20]), ("John", [Person "John" 20])] groupBy pAge people `shouldBe` [(10, [Person "Jane" 10]), (20, [Person "Jane" 20, Person "John" 20])] + + describe "stringToInt" $ + it "converts a String value to Maybe Int" $ do + stringToInt "12345678" `shouldBe` Just 12345678 + stringToInt "0" `shouldBe` Just 0 + stringToInt "10591" `shouldBe` Just 10591 + stringToInt "bad" `shouldBe` Nothing