packages feed

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 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