packages feed

yi-rope 0.2.1.12 → 0.2.2.0

raw patch · 4 files changed

+87/−3 lines, 4 files

Files

bench/MainBenchmarkSuite.hs view
@@ -2,10 +2,11 @@ {-# LANGUAGE OverloadedStrings #-} module Main where +import           Data.Char (isSpace) import           Control.DeepSeq import           Criterion.Main import qualified Criterion.Main as C-import           Data.Text (unlines, Text, replicate, unpack)+import           Data.Text (unlines, Text, replicate) import           Prelude hiding (unlines) import qualified Yi.Rope as F @@ -133,6 +134,23 @@   , onTextGroup "words" F.words   ] +spanBreakBench :: [Benchmark]+spanBreakBench =+  [ onTextGroup "spanTrue" $ F.span (const True)+  , onTextGroup "spanFalse" $ F.span (const False)+  , onTextGroup "spanSpace" $ F.span isSpace+  , onTextGroup "breakTrue" $ F.break (const True)+  , onTextGroup "breakFalse" $ F.break (const False)+  , onTextGroup "breakSpace" $ F.break isSpace+  ]++foldBench :: [Benchmark]+foldBench =+  [ onTextGroup "foldCount" $ F.foldl' (\x _ -> x + 1) (0 :: Integer)+  , onTextGroup "foldId" $ F.foldl' F.snoc F.empty+  , onTextGroup "foldReverse" $ F.foldl' (\x y -> F.cons y x) F.empty+  ]+ main :: IO () main = defaultMain $   [ onIntGroup "drop" F.drop@@ -157,5 +175,9 @@   , onTextGroup "any bad, (== '×')" $ F.any (== '×')   , onTextGroup "all OK (/= '×')" $ F.all (== '×')   , onTextGroup "all bad, (== '中')" $ F.all (== '中')+  , onTextGroup "init" F.init+  , onTextGroup "tail" F.tail   ] ++ splitBench     ++ wordsBench+    ++ spanBreakBench+    ++ foldBench
src/Yi/Rope.hs view
@@ -44,7 +44,8 @@    Yi.Rope.intercalate, Yi.Rope.intersperse,    Yi.Rope.filter, Yi.Rope.map,    Yi.Rope.words, Yi.Rope.unwords,-   Yi.Rope.split,+   Yi.Rope.split, Yi.Rope.init, Yi.Rope.tail,+   Yi.Rope.span, Yi.Rope.break, Yi.Rope.foldl',     -- * IO    Yi.Rope.readFile, Yi.Rope.readFile', Yi.Rope.writeFile,@@ -279,6 +280,22 @@   EmptyR -> Nothing   _ :> Chunk _ x -> if TX.null x then Nothing else Just (TX.last x) +-- | Takes every character but the last one: returns Nothing on empty+-- string.+init :: YiString -> Maybe YiString+init (YiString t) = case viewr t of+  EmptyR -> Nothing+  ts :> Chunk 0 _ -> Yi.Rope.init (YiString ts)+  ts :> Chunk l x -> Just . YiString $ ts |- Chunk (l - 1) (TX.init x)++-- | Takes the tail of the underlying string. If the string is empty+-- to begin with, returns Nothing.+tail :: YiString -> Maybe YiString+tail (YiString t) = case viewr t of+  EmptyR -> Nothing+  ts :> Chunk 0 _ -> Yi.Rope.tail (YiString ts)+  ts :> Chunk l x -> Just . YiString $ ts |- Chunk (l - 1) (TX.tail x)+ -- | Splits the string at given character position. -- -- If @position <= 0@ then the left string is empty and the right string@@ -393,6 +410,24 @@           r = TX.reverse . TX.takeWhile p . TX.reverse $ x           l' = TX.length r ++-- | Returns a pair whose first element is the longest prefix+-- (possibly empty) of t of elements that satisfy p, and whose second+-- is the remainder of the string. See also 'TX.span'.+--+-- This implementation uses 'Yi.Rope.splitAt' which actually is just+-- as fast as hand-unrolling the tree. GHC sure is great!+span :: (Char -> Bool) -> YiString -> (YiString, YiString)+span p y = let x = Yi.Rope.takeWhile p y+           in case Yi.Rope.splitAt (Yi.Rope.length x) y of+             -- Re-using ‘x’ seems to gain us a minor performance+             -- boost.+             (_, y') -> (x, y')++-- | Just like 'Yi.Rope.span' but with the predicate negated.+break :: (Char -> Bool) -> YiString -> (YiString, YiString)+break p = Yi.Rope.span (not . p)+ -- | Concatenates the list of 'YiString's after inserting the -- user-provided 'YiString' between the elements. --@@ -644,6 +679,18 @@ -- in itself. split :: (Char -> Bool) -> YiString -> [YiString] split p = fmap fromText . TX.split p . toText++-- | Left fold.+--+-- Benchmarks show that folding is actually Pretty Damn Slow™: consider+-- whether folding is really the best thing to use in your scenario.+foldl' :: (a -> Char -> a) -> a -> YiString -> a+foldl' f a = go a . fromRope+  where+    go acc t = case viewl t of+      EmptyL -> acc+      Chunk _ x :< ts -> let r = TX.foldl' f acc x+                         in r `seq` go r ts  -- | Helper function doing conversions of to and from underlying -- 'TX.Text'. You should aim to implement everything in terms of
test/Yi/RopeSpec.hs view
@@ -84,3 +84,18 @@       R.toText (R.unwords (R.fromText <$> t)) `shouldBe` T.unwords t     prop "\\p -> R.split p ~ T.split p $ isUpper" $ \t ->       R.toText <$> R.split isUpper (R.fromText t) `shouldBe` T.split isUpper t+    prop "non-empty s ⊢ R.init s ~ T.init s" $ \t ->+      let t' = t `T.snoc` 'a' -- ensure non-empty+      in (fmap R.toText . R.init . R.fromText) t' `shouldBe` (Just . T.init) t'+    prop "non-empty s ⊢ R.tail s ~ T.tail s" $ \t ->+      let t' = t `T.snoc` 'a'+      in (fmap R.toText . R.tail . R.fromText) t' `shouldBe` (Just . T.tail) t'+    prop "\\p -> R.span p ~ T.span p $ isUpper" $ \t ->+      let (f, s) `shouldBeT` y = (R.toText f, R.toText s) `shouldBe` y+      in (R.span isUpper . R.fromText) t `shouldBeT` T.span isUpper t+    prop "\\p -> R.break p ~ T.break p $ isUpper" $ \t ->+      let (f, s) `shouldBeT` y = (R.toText f, R.toText s) `shouldBe` y+      in (R.break isUpper . R.fromText) t `shouldBeT` T.break isUpper t+    prop "\\p -> R.foldl' p ~ T.foldl' p $ \\x _ -> x + 1" $ \t ->+      let f x _ = x + (1 :: Integer)+      in (R.foldl' f 0 . R.fromText) t `shouldBe` T.foldl' f 0 t
yi-rope.cabal view
@@ -1,5 +1,5 @@ name:                yi-rope-version:             0.2.1.12+version:             0.2.2.0 synopsis:            A rope data structure used by Yi description:         A rope data structure used by Yi license:             GPL-3