on master help sign in
Fix up the type of find, and add a break function.
0e92d3fBryan O'Sullivan
Data/Text.hs+44−18
@@ -115,6 +115,7 @@ module Data.Text
115 , stripEnd
115 , stripEnd
116 , splitAt
116 , splitAt
117 , spanBy
117 , spanBy
118+ , break
118 , breakBy
119 , breakBy
119 , group
120 , group
120 , groupBy
121 , groupBy
@@ -124,7 +125,6 @@ module Data.Text
124 -- ** Breaking into many substrings
125 -- ** Breaking into many substrings
125 -- $split
126 -- $split
126 , split
127 , split
127− , splitTimes
128 , splitTimesEnd
128 , splitTimesEnd
129 , splitBy
129 , splitBy
130 , chunksOf
130 , chunksOf
@@ -1051,35 +1051,62 @@ filter :: (Char -> Bool) -> Text -> Text
1051filter p t = unstream (S.filter p (stream t))
1051filter p t = unstream (S.filter p (stream t))
1052{-# INLINE filter #-}
1052{-# INLINE filter #-}
1053
1053
1054−-- /O(n+m)/ Find all non-overlapping instances of @needle@ in
1054+-- | /O(n+m)/ Find the first instance of @needle@ in @haystack@. The
1055−-- @haystack@. The first element of the returned tuple is the prefix
1055+-- first element of the returned tuple is the prefix of @haystack@
1056−-- of @haystack@ before any matches of @needle@. The second element
1056+-- before @needle@ is matched. The second is the remainder of
1057−-- of the tuple is a list of non-overlapping substrings from
1057+-- @haystack@, starting with the match.
1058−-- @haystack@. Each begins with @needle@ and ends just before the
1059−-- following match. If @needle@ is not present in @haystack@, the
1060−-- list will be empty and the prefix will contain @haystack@.
1061--
1058--
1062-- Examples:
1059-- Examples:
1063--
1060--
1064−-- > find "::" "a::b::c" ==> ("a", ["::b", "::c"])
1061+-- > break "::" "a::b::c" ==> ("a", "::b::c")
1065−-- > find "/" "foobar" ==> ("foobar", [])
1062+-- > break "/" "foobar" ==> ("foobar", "")
1066--
1063--
1067-- Laws:
1064-- Laws:
1068--
1065--
1069−-- > concat (prefix : matches) == haystack
1066+-- > append prefix match == haystack
1070−-- > where (prefix, matches) = find needle haystack
1067+-- > where (prefix, match) = break needle haystack
1068+--
1069+-- If you need to break a string by a substring repeatedly (e.g. you
1070+-- want to break on every instance of a substring), use 'find'
1071+-- instead, as it has lower startup overhead.
1071--
1072--
1072-- In (unlikely) bad cases, this function's time complexity
1073-- In (unlikely) bad cases, this function's time complexity
1073-- degenerates towards /O(n*m)/.
1074-- degenerates towards /O(n*m)/.
1075+break :: Text -> Text -> (Text, Text)
1076+break pat src@(Text arr off len)
1077+ | null pat = emptyError "break"
1078+ | otherwise = case indices pat src of
1079+ [] -> (src, empty)
1080+ (x:_) -> (textP arr off x, textP arr (off+x) (len-x))
1081+{-# INLINE break #-}
1082+
1083+-- | /O(n+m)/ Find all non-overlapping instances of @needle@ in
1084+-- @haystack@. The first element of the returned pair is the prefix
1085+-- of @haystack@ prior to any matches of @needle@. The second is a
1086+-- list of pairs.
1087+--
1088+-- The first element of each pair in the list is a span from the
1089+-- beginning of a match to the beginning of the next match, while the
1090+-- second is a span from the beginning of the match to the end of the
1091+-- input.
1092+--
1093+-- Examples:
1094+--
1095+-- > find "::" ""
1096+-- > ==> ("", [])
1097+-- > find "/" "a/b/c/d"
1098+-- > ==> ("a", [("/b","/b/c/d"), ("/c","/c/d"), ("/d","/d")])
1074−find :: Text -> Text -> (Text, [Text])
1099+find :: Text -> Text -> (Text, [(Text, Text)])
1075find pat src@(Text arr off len)
1100find pat src@(Text arr off len)
1076 | null pat = emptyError "find"
1101 | null pat = emptyError "find"
1077 | otherwise = case indices pat src of
1102 | otherwise = case indices pat src of
1078 [] -> (src, [])
1103 [] -> (src, [])
1079− (x:xs) -> (textP arr off x, go x xs)
1104+ (x:xs) -> (chunk 0 x, go x xs)
1080 where
1105 where
1081− go !s (x:xs) = textP arr (s+off) (x-s) : go x xs
1106+ go !s (x:xs) = (chunk s (x-s), chunk s (len-s)) : go x xs
1082− go s _ = [textP arr (s+off) (len-s)]
1107+ go s _ = let c = chunk s (len-s)
1108+ in [(c,c)]
1109+ chunk !n !l = textP arr (n+off) l
1083{-# INLINE find #-}
1110{-# INLINE find #-}
1084
1111
1085-------------------------------------------------------------------------------
1112-------------------------------------------------------------------------------
@@ -1099,8 +1126,7 @@ find pat src@(Text arr off len)
1099-- For example, suppose you have a string that you want to split on
1126-- For example, suppose you have a string that you want to split on
1100-- the substring @\"::\"@, such as @\"foo::bar::quux\"@. Instead of
1127-- the substring @\"::\"@, such as @\"foo::bar::quux\"@. Instead of
1101-- searching for the index of @\"::\"@ and taking the substrings
1128-- searching for the index of @\"::\"@ and taking the substrings
1102−-- before and after that index, you would instead use @splitTimes 1
1129+-- before and after that index, you would instead use @find "::"@.
1103−-- "::"@.
1104
1130
1105-- | /O(n)/ 'Text' index (subscript) operator, starting from 0.
1131-- | /O(n)/ 'Text' index (subscript) operator, starting from 0.
1106index :: Text -> Int -> Char
1132index :: Text -> Int -> Char
tests/Properties.hs+1−17
@@ -399,24 +399,12 @@ t_tails = L.tails `eqP` (map unpackS . T.tails)
399tl_tails = L.tails `eqP` (map unpackS . TL.tails)
399tl_tails = L.tails `eqP` (map unpackS . TL.tails)
400
400
401findSplit s t = case T.find s t of
401findSplit s t = case T.find s t of
402− (x,xs) -> x : L.map (T.drop (T.length s)) xs
402+ (x,xs) -> x : L.map (T.drop (T.length s) . fst) xs
403
403
404t_findSplit s = T.split s `eq` findSplit s
404t_findSplit s = T.split s `eq` findSplit s
405t_split_split s = T.split s `eq` Slow.split s
405t_split_split s = T.split s `eq` Slow.split s
406t_split_i (NotEmpty t) = id `eq` (T.intercalate t . T.split t)
406t_split_i (NotEmpty t) = id `eq` (T.intercalate t . T.split t)
407tl_split_i (NotEmpty t) = id `eq` (TL.intercalate t . TL.split t)
407tl_split_i (NotEmpty t) = id `eq` (TL.intercalate t . TL.split t)
408−t_splitTimes_i k (NotEmpty t) = id `eq` (T.intercalate t . T.splitTimes k t)
409−tl_splitTimes_i k (NotEmpty t) = id `eq` (TL.intercalate t . TL.splitTimes k t)
410−t_splitTimes_split k (NotEmpty t) =
411− T.splitTimes k t `eq` \u ->
412− case L.splitAt k (T.split t u) of
413− (a,[]) -> a
414− (a,b) -> a ++ [T.intercalate t b]
415−tl_splitTimes_split k (NotEmpty t) =
416− TL.splitTimes k t `eq` \u ->
417− case L.splitAt (fromIntegral k) (TL.split t u) of
418− (a,[]) -> a
419− (a,b) -> a ++ [TL.intercalate t b]
420t_splitTimesEnd_i k (NotEmpty t) = id `eq` (T.intercalate t . T.splitTimesEnd k t)
408t_splitTimesEnd_i k (NotEmpty t) = id `eq` (T.intercalate t . T.splitTimesEnd k t)
421tl_splitTimesEnd_i k (NotEmpty t) = id `eq` (TL.intercalate t . TL.splitTimesEnd k t)
409tl_splitTimesEnd_i k (NotEmpty t) = id `eq` (TL.intercalate t . TL.splitTimesEnd k t)
422t_splitTimesEnd_split (NotEmpty t) = T.splitTimesEnd maxBound t `eq` T.split t
410t_splitTimesEnd_split (NotEmpty t) = T.splitTimesEnd maxBound t `eq` T.split t
@@ -798,10 +786,6 @@ tests = [
798 testProperty "t_findSplit" t_findSplit,
786 testProperty "t_findSplit" t_findSplit,
799 testProperty "t_split_split" t_split_split,
787 testProperty "t_split_split" t_split_split,
800 testProperty "t_split_i" t_split_i,
788 testProperty "t_split_i" t_split_i,
801− testProperty "t_splitTimes_i" t_splitTimes_i,
802− testProperty "tl_splitTimes_i" tl_splitTimes_i,
803− testProperty "t_splitTimes_split" t_splitTimes_split,
804− testProperty "tl_splitTimes_split" tl_splitTimes_split,
805 testProperty "t_splitTimesEnd_i" t_splitTimesEnd_i,
789 testProperty "t_splitTimesEnd_i" t_splitTimesEnd_i,
806 testProperty "tl_splitTimesEnd_i" tl_splitTimesEnd_i,
790 testProperty "tl_splitTimesEnd_i" tl_splitTimesEnd_i,
807 testProperty "t_splitTimesEnd_split" t_splitTimesEnd_split,
791 testProperty "t_splitTimesEnd_split" t_splitTimesEnd_split,