vector-split (empty) → 1.0.0.0
raw patch · 6 files changed
+726/−0 lines, 6 filesdep +QuickCheckdep +basedep +splitsetup-changed
Dependencies added: QuickCheck, base, split, tasty, tasty-quickcheck, vector, vector-split
Files
- LICENSE +30/−0
- Setup.hs +2/−0
- src/Data/Vector/Split.hs +124/−0
- src/Data/Vector/Split/Internal.hs +338/−0
- test/Spec.hs +183/−0
- vector-split.cabal +49/−0
+ LICENSE view
@@ -0,0 +1,30 @@+Copyright Florian Hofmann (c) 2016++All rights reserved.++Redistribution and use in source and binary forms, with or without+modification, are permitted provided that the following conditions are met:++ * Redistributions of source code must retain the above copyright+ notice, this list of conditions and the following disclaimer.++ * Redistributions in binary form must reproduce the above+ copyright notice, this list of conditions and the following+ disclaimer in the documentation and/or other materials provided+ with the distribution.++ * Neither the name of Florian Hofmann nor the names of other+ contributors may be used to endorse or promote products derived+ from this software without specific prior written permission.++THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ Setup.hs view
@@ -0,0 +1,2 @@+import Distribution.Simple+main = defaultMain
+ src/Data/Vector/Split.hs view
@@ -0,0 +1,124 @@++++module Data.Vector.Split+ ( chunksOf+ , splitPlaces+ , splitPlacesBlanks+ , chop+ , divvy+ , module Data.Vector.Split.Internal+ ) where+++import Data.Vector.Generic (Vector)+import qualified Data.Vector.Generic as V++import Data.List (unfoldr)++import Data.Vector.Split.Internal++++-- | @'chunksOf' n@ splits a vector into length-n pieces. The last+-- piece will be shorter if @n@ does not evenly divide the length of+-- the vector. If @n <= 0@, @'chunksOf' n l@ returns an infinite list+-- of empty vectors. For example:+--+-- Note that @'chunksOf' n []@ is @[]@, not @[[]]@. This is+-- intentional, and is consistent with a recursive definition of+-- 'chunksOf'; it satisfies the property that+--+-- @chunksOf n xs ++ chunksOf n ys == chunksOf n (xs ++ ys)@+--+-- whenever @n@ evenly divides the length of @xs@.+chunksOf :: Vector v a => Int -> v a -> [v a]+chunksOf i = unfoldr go+ where go v | V.null v = Nothing+ | otherwise = Just (V.splitAt i v)++-- | Split a vector into chunks of the given lengths. For example:+--+-- > splitPlaces [2,3,4] [1..20] == [[1,2],[3,4,5],[6,7,8,9]]+-- > splitPlaces [4,9] [1..10] == [[1,2,3,4],[5,6,7,8,9,10]]+-- > splitPlaces [4,9,3] [1..10] == [[1,2,3,4],[5,6,7,8,9,10]]+--+-- If the input vector is longer than the total of the given lengths,+-- then the remaining elements are dropped. If the vector is shorter+-- than the total of the given lengths, then the result may contain+-- fewer chunks than requested, and the last chunk may be shorter+-- than requested.+splitPlaces :: Vector v a => [Int] -> v a -> [v a]+splitPlaces is v = unfoldr go (is,v)+ where go ([],_) = Nothing+ go (i:is,v) | V.null v = Nothing+ | otherwise = let (l,r) = V.splitAt i v in Just (l,(is,r))+++-- | Split a vector into chunks of the given lengths. Unlike+-- 'splitPlaces', the output list will always be the same length as+-- the first input argument. If the input vector is longer than the+-- total of the given lengths, then the remaining elements are+-- dropped. If the vector is shorter than the total of the given+-- lengths, then the last several chunks will be shorter than+-- requested or empty. For example:+--+-- > splitPlacesBlanks [2,3,4] [1..20] == [[1,2],[3,4,5],[6,7,8,9]]+-- > splitPlacesBlanks [4,9] [1..10] == [[1,2,3,4],[5,6,7,8,9,10]]+-- > splitPlacesBlanks [4,9,3] [1..10] == [[1,2,3,4],[5,6,7,8,9,10],[]]+--+-- Notice the empty list in the output of the third example, which+-- differs from the behavior of 'splitPlaces'.+splitPlacesBlanks :: Vector v a => [Int] -> v a -> [v a]+splitPlacesBlanks is v = unfoldr go (is,v)+ where go ([],_) = Nothing+ go (i:is,v) = let (l,r) = V.splitAt i v in Just (l,(is,r))++++-- | A useful recursion pattern for processing a list to produce a new+-- list, often used for \"chopping\" up the input list. Typically+-- chop is called with some function that will consume an initial+-- prefix of the list and produce a value and the rest of the list.+--+-- For example, many common Prelude functions can be implemented in+-- terms of @chop@:+--+-- > group :: (Eq a) => [a] -> [[a]]+-- > group = chop (\ xs@(x:_) -> span (==x) xs)+-- >+-- > words :: String -> [String]+-- > words = filter (not . null) . chop (span (not . isSpace) . dropWhile isSpace)+chop :: Vector v a => (v a -> (b, v a)) -> v a -> [b]+chop f v | V.null v = []+ | otherwise = b : chop f v'+ where (b, v') = f v++-- | Divides up an input vector into a set of subvectors, according to 'n' and 'm'+-- input specifications you provide. Each subvector will have 'n' items, and the+-- start of each subvector will be offset by 'm' items from the previous one.+--+-- > divvy 5 5 [1..20] == [[1,2,3,4,5],[6,7,8,9,10],[11,12,13,14,15],[16,17,18,19,20]]+--+-- In the case where a source vector's trailing elements do no fill an entire+-- subvector, those trailing elements will be dropped.+--+-- > divvy 5 2 [1..10] == [[1,2,3,4,5],[3,4,5,6,7],[5,6,7,8,9]]+--+-- As an example, you can generate a moving average over a vector of prices:+-- +-- > type Prices = [Float]+-- > type AveragePrices = [Float]+-- > +-- > average :: [Float] -> Float+-- > average xs = sum xs / (fromIntegral $ length xs)+-- > +-- > simpleMovingAverage :: Prices -> AveragePrices+-- > simpleMovingAverage priceList =+-- > map average divvyedPrices+-- > where divvyedPrices = divvy 20 1 priceList+divvy :: Vector v a => Int -> Int -> v a -> [v a]+divvy n m v | V.null v = []+ | otherwise = filter (\ws -> n == V.length ws)+ . chop (\xs -> (V.take n xs, V.drop m xs))+ $ v
+ src/Data/Vector/Split/Internal.hs view
@@ -0,0 +1,338 @@++++module Data.Vector.Split.Internal + ( split+ , Splitter(..)+ , splitOn+ , splitOneOf+ , splitWhen+ , endBy+ , oneOf+ , endByOneOf+ , wordsBy+ , linesBy+ , onSublist+ , whenElt+ , dropDelims+ , keepDelimsL+ , keepDelimsR+ , condense+ , dropInitBlank+ , dropInnerBlanks+ , dropFinalBlank+ , dropBlanks+ , startsWith+ , startsWithOneOf+ , endsWith+ , endsWithOneOf+ ) where+++import Data.Vector.Generic (Vector)+import qualified Data.Vector.Generic as V++import qualified Data.Vector as BV++import qualified Data.List as L++-- | A delimiter is a list of predicates on elements, matched by some+-- contiguous subsequence of a list.+newtype Delimiter a = Delimiter (BV.Vector (a -> Bool))++-- | Try to match a delimiter at the start of a list, either failing+-- or decomposing the list into the portion which matched the delimiter+-- and the remainder.+matchDelim :: Vector v a => Delimiter a -> v a -> Maybe (v a, v a)+matchDelim (Delimiter ds) xs = if match && lengthOk then Just (V.splitAt (V.length ds) xs) else Nothing+ where match = V.and $ V.zipWith ($) ds (V.convert xs)+ lengthOk = V.length ds <= V.length xs+++++data Chunk v a = Delim (v a) | Text (v a)+ deriving (Show, Eq)+++fromChunk :: Chunk v a -> v a+fromChunk (Delim d) = d+fromChunk (Text t) = t++isDelim :: Chunk v a -> Bool+isDelim (Delim _) = True+isDelim (Text _) = False++type SplitList v a = [Chunk v a]++type Splitter v a = v a -> SplitList v a+++++toSplitList :: Vector v a => Delimiter a -> v a -> SplitList v a+toSplitList d v = go 0+ where go i | i < V.length v = case matchDelim d (V.drop i v) of+ Just (l,r) -> Text (V.take i v) : Delim l : toSplitList d r+ Nothing -> go (i+1)+ | otherwise = [Text v]+++split :: Vector v a => Splitter v a -> v a -> [v a]+split s v = map go $ s v+ where go (Text t) = t+ go (Delim d) = d+++-- Convenience functions++-- | Split on the given sublist. Equivalent to split . dropDelims . onSublist. For example:+--+-- >>> splitOn (BV.fromList "..") (BV.fromList "a..b...c....d..")+-- ["a","b",".c","","d",""]+-- +-- In some parsing combinator frameworks this is also known as sepBy.+--+-- Note that this is the right inverse of the intercalate function from Data.List, that is,+--+-- >> \xs -> (intercalate xs . splitOn xs) === id+--+-- splitOn x . intercalate x is the identity on certain lists, but it is+-- tricky to state the precise conditions under which this holds. (For+-- example, it is not enough to say that x does not occur in any elements+-- of the input list. Working out why is left as an exercise for the+-- reader.)+splitOn :: (Vector v a, Eq a) => v a -> v a -> [v a]+splitOn xs = split (dropDelims . onSublist xs)++-- | Split on any of the given elements. Equivalent to split . dropDelims . oneOf. For example:+--+-- >>> splitOneOf (BV.fromList ";.,") (BV.fromList "foo,bar;baz.glurk")+-- ["foo","bar","baz","glurk"]+splitOneOf :: (Vector v a, Eq a) => v a -> v a -> [v a]+splitOneOf xs = split (dropDelims . oneOf xs)++-- | Split on elements satisfying the given predicate. Equivalent to split . dropDelims . whenElt. For example:+--+-- >>> splitWhen (<0) (BV.fromList [1,3,-4,5,7,-9,0,2])+-- [[1,3],[5,7],[0,2]]+splitWhen :: (Vector v a) => (a -> Bool) -> v a -> [v a]+splitWhen p = split (dropDelims . whenElt p)++-- | Split into chunks terminated by the given subsequence. Equivalent to split . dropFinalBlank . dropDelims . onSublist. For example:+--+-- >>> endBy (BV.fromList ";") (BV.fromList "foo;bar;baz;")+-- ["foo","bar","baz"]+--+-- Note also that the lines function from Data.List is equivalent to endBy "\n".+endBy :: (Vector v a, Eq a) => v a -> v a -> [v a]+endBy xs = split (dropFinalBlank . dropDelims . onSublist xs)+-- Basic Strategies++-- | A splitting strategy that splits on any one of the given elements. For example:+-- >>> split (oneOf (BV.fromList "xyz")) (BV.fromList "aazbxyzcxd")+-- ["aa","z","b","x","","y","","z","c","x","d"]+oneOf :: (Vector v a, Eq a) => v a -> Splitter v a+oneOf xs = toSplitList delim+ where delim = Delimiter (BV.singleton (`V.elem` xs))+++-- | Split into chunks terminated by one of the given elements. Equivalent to split . dropFinalBlank . dropDelims . oneOf. For example:+--+-- >>> endByOneOf (BV.fromList ";,") (BV.fromList "foo;bar,baz;") +-- ["foo","bar","baz"]+endByOneOf :: (Vector v a, Eq a) => v a -> v a -> [v a]+endByOneOf xs = split (dropFinalBlank . dropDelims . oneOf xs)+++-- | Split into "words", with word boundaries indicated by the given predicate. Satisfies words === wordsBy isSpace; equivalent to split . dropBlanks . dropDelims . whenElt. For example:+--+-- >>> wordsBy (=='x') (BV.fromList "dogxxxcatxbirdxx") +-- ["dog","cat","bird"]+wordsBy :: Vector v a => (a -> Bool) -> v a -> [v a]+wordsBy p = split (dropBlanks . dropDelims . whenElt p)+++-- | Split into "lines", with line boundaries indicated by the given predicate. Satisfies lines === linesBy (=='\n'); equivalent to split . dropFinalBlank . dropDelims . whenElt. For example:+--+-- >>> linesBy (=='x') (BV.fromList "dogxxxcatxbirdxx")+-- ["dog","","","cat","bird",""]+linesBy :: Vector v a => (a -> Bool) -> v a -> [v a]+linesBy p = split (dropFinalBlank . dropDelims . whenElt p)+++-- | A splitting strategy that splits on the given list, when it is+-- encountered as an exact subsequence. For example:+--+-- >>> split (onSublist (BV.fromList "xyz")) (BV.fromList "aazbxyzcxd")+-- ["aazb","xyz","cxd"]+--+-- Note that splitting on the empty list is not allowed in `vector-split`.+-- This is a major difference between `split` and `vector-split`. In any+-- case nobody should use `vector-split` to do this anyway.+onSublist :: (Vector v a, Eq a) => v a -> Splitter v a+onSublist xs = toSplitList delim+ where delim = Delimiter (V.map (==) . V.convert $ xs)++-- | A splitting strategy that splits on any elements that satisfy the given predicate. For example:+--+-- >>> split (whenElt (<0)) (BV.fromList [2,4,-3,6,-9,1])+-- [[2,4],[-3],[6],[-9],[1]]+whenElt :: (Vector v a) => (a -> Bool) -> Splitter v a+whenElt p = toSplitList delim+ where delim = Delimiter (V.singleton p)+++-- Strategy Transformers++-- | Drop delimiters from the output (the default is to keep them). For example,+--+-- >>> split (oneOf (BV.fromList ":")) (BV.fromList "a:b:c")+-- ["a",":","b",":","c"]+-- >>> split (dropDelims . oneOf (BV.fromList ":")) (BV.fromList "a:b:c")+-- ["a","b","c"]+dropDelims :: Vector v a => SplitList v a -> SplitList v a+dropDelims = filter go + where go (Delim _) = False+ go (Text _ ) = True++-- | Keep delimiters in the output by prepending them to adjacent chunks. For example:+--+-- >>> split (keepDelimsL . oneOf (BV.fromList "xyz")) (BV.fromList "aazbxyzcxd")+-- ["aa","zb","x","y","zc","xd"]+keepDelimsL :: Vector v a => SplitList v a -> SplitList v a+keepDelimsL (Delim d : Text t : rst) = Text (d V.++ t) : keepDelimsL rst+keepDelimsL (rs : rst) = rs : keepDelimsL rst+keepDelimsL [] = []++-- | Keep delimiters in the output by appending them to adjacent chunks. For example:+--+-- >>> split (keepDelimsR . oneOf (BV.fromList "xyz")) (BV.fromList "aazbxyzcxd")+-- ["aaz","bx","y","z","cx","d"]+keepDelimsR :: Vector v a => SplitList v a -> SplitList v a+keepDelimsR (Text t : Delim d : rst) = Text (t V.++ d) : keepDelimsR rst+keepDelimsR (rs : rst) = rs : keepDelimsR rst+keepDelimsR [] = []++-- FIXME really not sure about the delim : text : delim case ... +-- this was inserted to be compatible with the split package++-- | Condense multiple consecutive delimiters into one. For example:+--+-- >>> split (condense . oneOf (BV.fromList "xyz")) (BV.fromList "aazbxyzcxd")+-- ["aa","z","b","xyz","c","x","d"]+-- >>> split (dropDelims . oneOf (BV.fromList "xyz")) (BV.fromList "aazbxyzcxd")+-- ["aa","b","","","c","d"]+-- >>> split (condense . dropDelims . oneOf (BV.fromList "xyz")) (BV.fromList "aazbxyzcxd")+-- ["aa","b","c","d"]+--+-- FIXME this function is not fully compatible with the Data.List.Split version.+condense :: Vector v a => SplitList v a -> SplitList v a+condense (Delim a : Delim b : rst) = condense (Delim (a V.++ b) : rst)+condense (Delim a : Text t : Delim b : rst) | V.null t = condense (Delim (a V.++ b) : rst)+condense (t : rst) = t : condense rst+condense [] = []++-- | Don't generate a blank chunk if there is a delimiter at the beginning. For example:+--+-- >>> split (oneOf (BV.fromList ":")) (BV.fromList ":a:b")+-- ["",":","a",":","b"]+-- >>> split (dropInitBlank . oneOf (BV.fromList ":")) (BV.fromList ":a:b")+-- [":","a",":","b"]+dropInitBlank :: Vector v a => SplitList v a -> SplitList v a+dropInitBlank (Text t : rst) | V.null t = rst+dropInitBlank rst = rst++-- | Don't generate a blank chunk if there is a delimiter at the end. For example:+--+-- split (oneOf (BV.fromList ":")) (BV.fromList "a:b:")+-- ["a",":","b",":",""]+-- split (dropFinalBlank . oneOf (BV.fromList ":")) (BV.fromList "a:b:")+-- ["a",":","b",":"]+dropFinalBlank :: Vector v a => SplitList v a -> SplitList v a+dropFinalBlank [Text t] | V.null t = []+dropFinalBlank (rs : rst) = rs : dropFinalBlank rst+dropFinalBlank [] = []++-- | Don't generate blank chunks between consecutive delimiters. For example:+--+-- >>> split (oneOf (BV.fromList ":")) (BV.fromList "::b:::a")+-- ["",":","",":","b",":","",":","",":","a"]+-- >>> split (dropInnerBlanks . oneOf (BV.fromList ":")) (BV.fromList "::b:::a")+-- ["",":",":","b",":",":",":","a"]+dropInnerBlanks :: Vector v a => SplitList v a -> SplitList v a+dropInnerBlanks (Text a : rst) = Text a : dropInnerBlanksGo rst+dropInnerBlanks rst = dropInnerBlanksGo rst++dropInnerBlanksGo :: Vector v a => [Chunk v a] -> [Chunk v a]+dropInnerBlanksGo [Text a] = [Text a]+dropInnerBlanksGo (Text a : rst) | V.null a = dropInnerBlanksGo rst+dropInnerBlanksGo (chunk : rst) = chunk : dropInnerBlanksGo rst+dropInnerBlanksGo [] = []+++-- Derived combinators++-- | Drop all blank chunks from the output, and condense consecutive+-- delimiters into one. +-- Equivalent to dropInitBlank . dropFinalBlank . condense. For example:+--+-- >>> split (oneOf (BV.fromList ":")) (BV.fromList "::b:::a")+-- ["",":","",":","b",":","",":","",":","a"]+-- >>> split (dropBlanks . oneOf (BV.fromList ":")) (BV.fromList "::b:::a")+-- ["::","b",":::","a"]+dropBlanks :: Vector v a => SplitList v a -> SplitList v a+dropBlanks = condense . filter go+ where go (Text t) | V.null t = False+ go _ = True++-- | Make a strategy that splits a list into chunks that all start with the+-- given subsequence (except possibly the first). Equivalent to+-- dropInitBlank . keepDelimsL . onSublist. For example:+--+-- >>> split (startsWith (BV.fromList "app")) (BV.fromList "applyapplicativeapplaudapproachapple")+-- ["apply","applicative","applaud","approach","apple"]+startsWith :: (Vector v a, Eq a) => v a -> Splitter v a+startsWith xs = dropInitBlank . keepDelimsL . onSublist xs++-- | Make a strategy that splits a list into chunks that all start with one+-- of the given elements (except possibly the first). Equivalent to+-- dropInitBlank . keepDelimsL . oneOf. For example:+--+-- >>> split (startsWithOneOf (BV.fromList ['A'..'Z'])) (BV.fromList "ACamelCaseIdentifier")+-- ["A","Camel","Case","Identifier"]+startsWithOneOf :: (Vector v a, Eq a) => v a -> Splitter v a+startsWithOneOf xs = dropInitBlank . keepDelimsL . oneOf xs++-- | Make a strategy that splits a list into chunks that all end with the+-- given subsequence, except possibly the last. Equivalent to+-- dropFinalBlank . keepDelimsR . onSublist. For example:+--+-- >>> split (endsWith (BV.fromList "ly")) (BV.fromList "happilyslowlygnarlylily")+-- ["happily","slowly","gnarly","lily"]+endsWith :: (Vector v a, Eq a) => v a -> Splitter v a+endsWith xs = dropFinalBlank . keepDelimsR . onSublist xs++++-- Note: this function is not consistent with the Data.List.Split version++-- | Make a strategy that splits a list into chunks that all end with one of+-- the given elements, except possibly the last. Equivalent to+-- dropFinalBlank . keepDelimsR . oneOf. For example:+--+-- >>> split (condense . endsWithOneOf (BV.fromList ".,?! ")) (BV.fromList "Hi, there! How are you?")+-- ["Hi, ","there! ","How ","are ","you?"]+endsWithOneOf :: (Vector v a, Eq a) => v a -> Splitter v a+endsWithOneOf xs = dropFinalBlank . keepDelimsR . oneOf xs++++++-- Util++intercalateV :: Vector v a => v a -> [v a] -> v a+intercalateV d = go+ where go [x] = x+ go (x:xs) = x V.++ d V.++ go xs
+ test/Spec.hs view
@@ -0,0 +1,183 @@++{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE CPP #-}++import Test.Tasty+import qualified Test.Tasty.QuickCheck as QC++import Test.QuickCheck+import Test.QuickCheck.Function++import qualified Data.Vector as V+import qualified Data.Vector.Split as V+import qualified Data.Vector.Split.Internal as V++import qualified Data.List as L+import qualified Data.List.Split as L++import Data.Char++import Debug.Trace++main :: IO ()+main = defaultMain tests++++tests = testGroup "tests" + [ testGroup "basic strategies"+ [ QC.testProperty "oneOf" prop_oneOf+ , QC.testProperty "onSublist" prop_onSublist+ , QC.testProperty "whenElt" prop_whenElt+ ]+ , testGroup "strategy transformers"+ [ QC.testProperty "dropDelims" prop_dropDelims+ , QC.testProperty "keepDelimsL" prop_keepDelimsL+ , QC.testProperty "keepDelimsR" prop_keepDelimsR+ , QC.testProperty "condense" prop_condense+ , QC.testProperty "dropInitBlank" prop_dropInitBlank+ , QC.testProperty "dropFinalBlank" prop_dropFinalBlank+ , QC.testProperty "dropInnerBlank" prop_dropInnerBlank+ ]+ , testGroup "other splitting methods"+ [ QC.testProperty "chunksOf" prop_chunksOf+ , QC.testProperty "splitPlaces" prop_splitPlaces+ , QC.testProperty "splitPlacesBlanks" prop_splitPlacesBlanks+ , QC.testProperty "chop" prop_chop+ , QC.testProperty "chopGroup" prop_chopGroup+#if MIN_VERSION_split(2,3,0)+ , QC.testProperty "divvy" prop_divvy+#endif+ ]+ , testGroup "convenience functions"+ [ QC.testProperty "splitOn" prop_splitOn+ , QC.testProperty "splitOneOf" prop_splitOneOf+ , QC.testProperty "splitWhen" prop_splitWhen+ , QC.testProperty "endBy" prop_endBy+ , QC.testProperty "endByOneOf" prop_endByOneOf+ , QC.testProperty "wordsBy" prop_wordsBy+ , QC.testProperty "linesBy" prop_linesBy+ ]+ ]++-- small set of elements used for testing++newtype Elt = Elt { unElt :: Char } deriving (Eq)++type Elements = [Elt]+type SomeElements = NonEmptyList Elt++instance Show Elt where+ show (Elt c) = show c++instance Arbitrary Elt where+ arbitrary = elements (map Elt "abcde")++instance CoArbitrary Elt where+ coarbitrary = coarbitrary . ord . unElt+++instance Function Elt where+ function = functionMap unElt Elt++-- Basic Strategies++prop_oneOf :: Elements -> Elements -> Property+prop_oneOf d = listVsVec (L.split (L.oneOf d)) (V.split (V.oneOf (V.fromList d)))++prop_onSublist :: SomeElements -> Elements -> Property+prop_onSublist (NonEmpty d) = listVsVec (L.split (L.onSublist d)) (V.split (V.onSublist (V.fromList d)))++prop_whenElt :: Fun Elt Bool -> Elements -> Property+prop_whenElt (Fun _ f) = listVsVec (L.split (L.whenElt f)) (V.split (V.whenElt f))++++-- Strategy Transformers++prop_dropDelims :: SomeElements -> Elements -> Property+prop_dropDelims (NonEmpty d) = listVsVec (L.split (L.dropDelims (L.oneOf d)))+ (V.split (V.dropDelims . V.oneOf (V.fromList d)))++prop_keepDelimsL :: SomeElements -> Elements -> Property+prop_keepDelimsL (NonEmpty d) = listVsVec (L.split (L.keepDelimsL (L.oneOf d)))+ (V.split (V.keepDelimsL . V.oneOf (V.fromList d)))++prop_keepDelimsR :: SomeElements -> Elements -> Property+prop_keepDelimsR (NonEmpty d) = listVsVec (L.split (L.keepDelimsR (L.oneOf d)))+ (V.split (V.keepDelimsR . V.oneOf (V.fromList d)))++prop_condense :: SomeElements -> Elements -> Property+prop_condense (NonEmpty d) = listVsVec (L.split (L.condense (L.oneOf d)))+ (V.split (V.condense . V.oneOf (V.fromList d)))++prop_dropInitBlank :: SomeElements -> Elements -> Property+prop_dropInitBlank (NonEmpty d) = listVsVec (L.split (L.dropInitBlank (L.oneOf d)))+ (V.split (V.dropInitBlank . V.oneOf (V.fromList d)))++prop_dropFinalBlank :: SomeElements -> Elements -> Property+prop_dropFinalBlank (NonEmpty d) = listVsVec (L.split (L.dropFinalBlank (L.oneOf d)))+ (V.split (V.dropFinalBlank . V.oneOf (V.fromList d)))++prop_dropInnerBlank :: SomeElements -> Elements -> Property+prop_dropInnerBlank (NonEmpty d) = listVsVec (L.split (L.dropInnerBlanks (L.oneOf d)))+ (V.split (V.dropInnerBlanks . V.oneOf (V.fromList d)))++-- Other Splitting Methods++prop_chunksOf :: Positive Int -> [Double] -> Property+prop_chunksOf (Positive i) = listVsVec (L.chunksOf i) (V.chunksOf i)++prop_splitPlaces :: [Positive Int] -> [Double] -> Property+prop_splitPlaces is = listVsVec (L.splitPlaces is') (V.splitPlaces is')+ where is' = map getPositive is++prop_splitPlacesBlanks :: [Positive Int] -> [Double] -> Property+prop_splitPlacesBlanks is = listVsVec (L.splitPlacesBlanks is') (V.splitPlacesBlanks is')+ where is' = map getPositive is++prop_chop :: [Double] -> Property+prop_chop = listVsVec (L.chop (L.splitAt 10)) (V.chop (V.splitAt 10))++prop_chopGroup :: [Double] -> Property+prop_chopGroup = listVsVec (L.chop (\xs -> L.span (== L.head xs) xs)) (V.chop (\xs -> V.span (== V.head xs) xs))++#if MIN_VERSION_split(2,3,0)+prop_divvy :: Positive Int -> Positive Int -> [Double] -> Property+prop_divvy (Positive n) (Positive m) = listVsVec (L.divvy n m) (V.divvy n m)+#endif++++-- Convenience functions++prop_splitOneOf :: SomeElements -> Elements -> Property+prop_splitOneOf (NonEmpty ds) = listVsVec (L.splitOneOf ds) (V.splitOneOf (V.fromList ds))++prop_splitOn :: SomeElements -> Elements -> Property+prop_splitOn (NonEmpty ds) = listVsVec (L.splitOn ds) (V.splitOn (V.fromList ds))++prop_splitWhen :: Fun Elt Bool -> Elements -> Property+prop_splitWhen (Fun _ f) = listVsVec (L.splitWhen f) (V.splitWhen f)++prop_endBy :: SomeElements -> Elements -> Property+prop_endBy (NonEmpty p) = listVsVec (L.endBy p) (V.endBy (V.fromList p))++prop_endByOneOf :: SomeElements -> Elements -> Property+prop_endByOneOf (NonEmpty p) = listVsVec (L.endByOneOf p) (V.endByOneOf (V.fromList p))++prop_wordsBy :: Fun Elt Bool -> Elements -> Property+prop_wordsBy (Fun _ f) = listVsVec (L.wordsBy f) (V.wordsBy f)++prop_linesBy :: Fun Elt Bool -> Elements -> Property+prop_linesBy (Fun _ f) = listVsVec (L.linesBy f) (V.linesBy f)+++++-- | compare a list splitting vs a vector splitting function+listVsVec :: (Show a, Eq a) => ([a] -> [[a]]) -> (V.Vector a -> [V.Vector a]) -> [a] -> Property+listVsVec a b l = ls === map V.toList vs+ where v = V.fromList l+ ls = a l+ vs = b v
+ vector-split.cabal view
@@ -0,0 +1,49 @@+name: vector-split+version: 1.0.0.0+synopsis: Initial project template from stack+description: Please see README.md+homepage: https://github.com/fhaust/vector-split#readme+license: BSD3+license-file: LICENSE+author: Florian Hofmann+maintainer: fho@f12n.de+copyright: MIT+category: Web+build-type: Simple+-- extra-source-files:+cabal-version: >=1.10++library+ hs-source-dirs: src+ exposed-modules: Data.Vector.Split+ , Data.Vector.Split.Internal+ build-depends: base >= 4 && < 5+ , vector >= 0.10 && < 0.12+ default-language: Haskell2010+ ghc-options: -Wall++--executable vector-split-exe+-- hs-source-dirs: app+-- main-is: Main.hs+-- ghc-options: -threaded -rtsopts -with-rtsopts=-N+-- build-depends: base+-- , vector-split+-- default-language: Haskell2010++test-suite vector-split-test+ type: exitcode-stdio-1.0+ hs-source-dirs: test+ main-is: Spec.hs+ build-depends: base+ , vector+ , vector-split+ , split+ , tasty+ , tasty-quickcheck+ , QuickCheck+ ghc-options: -threaded -rtsopts -with-rtsopts=-N+ default-language: Haskell2010++source-repository head+ type: git+ location: https://github.com/fhaust/vector-split