on master help sign in
Fix up design and implementation of find
92d93c3Bryan O'Sullivan
Data/Text.hs+10−9
@@ -1086,30 +1086,31 @@ breakEnd pat src = let (a,b) = break (reverse pat) (reverse src)
1086{-# INLINE breakEnd #-}
1086{-# INLINE breakEnd #-}
1087
1087
1088-- | /O(n+m)/ Find all non-overlapping instances of @needle@ in
1088-- | /O(n+m)/ Find all non-overlapping instances of @needle@ in
1089−-- @haystack@. Each element of the returned list consists of a triple:
1089+-- @haystack@. Each element of the returned list consists of a pair:
1090--
1090--
1091−-- * The entire string prior to the /k/th match
1091+-- * The entire string prior to the /k/th match (i.e. the prefix)
1092--
1092--
1093−-- * The /k/th match
1093+-- * The /k/th match, followed by the remainder of the string
1094−--
1095−-- * The entire string following the /k/th match
1096--
1094--
1097-- Examples:
1095-- Examples:
1098--
1096--
1099-- > find "::" ""
1097-- > find "::" ""
1100-- > ==> []
1098-- > ==> []
1101-- > find "/" "a/b/c/"
1099-- > find "/" "a/b/c/"
1102−-- > ==> [("a", "/", "b/c/"), ("a/b", "/", "c/"), ("a/b/c", "/", "")]
1100+-- > ==> [("a", "/b/c/"), ("a/b", "/c/"), ("a/b/c", "/")]
1103--
1101--
1104-- In (unlikely) bad cases, this function's time complexity degrades
1102-- In (unlikely) bad cases, this function's time complexity degrades
1105-- towards /O(n*m)/.
1103-- towards /O(n*m)/.
1104+--
1105+-- The @needle@ parameter may not be empty.
1106+find :: Text -- ^ @needle@ to search for
1107+ -> Text -- ^ @haystack@ in which to search
1106−find :: Text -> Text -> [(Text, Text, Text)]
1108+ -> [(Text, Text)]
1107find pat@(Text _ _ plen) src@(Text arr off slen)
1109find pat@(Text _ _ plen) src@(Text arr off slen)
1108 | null pat = emptyError "find"
1110 | null pat = emptyError "find"
1109 | otherwise = L.map step (indices pat src)
1111 | otherwise = L.map step (indices pat src)
1110 where
1112 where
1111− step x = (chunk 0 x, chunk x plen, chunk (x+plen) (stop-x))
1113+ step x = (chunk 0 x, chunk x (slen-x))
1112− stop = slen - plen
1113 chunk !n !l = textP arr (n+off) l
1114 chunk !n !l = textP arr (n+off) l
1114{-# INLINE find #-}
1115{-# INLINE find #-}
1115
1116
Data/Text/Lazy.hs+11−9
@@ -915,33 +915,35 @@ breakEnd pat src = let (a,b) = break (reverse pat) (reverse src)
915{-# INLINE breakEnd #-}
915{-# INLINE breakEnd #-}
916
916
917-- | /O(n+m)/ Find all non-overlapping instances of @needle@ in
917-- | /O(n+m)/ Find all non-overlapping instances of @needle@ in
918−-- @haystack@. Each element of the returned list consists of a triple:
918+-- @haystack@. Each element of the returned list consists of a pair:
919--
919--
920−-- * The entire string prior to the /k/th match
920+-- * The entire string prior to the /k/th match (i.e. the prefix)
921--
921--
922−-- * The /k/th match
922+-- * The /k/th match, followed by the remainder of the string
923−--
924−-- * The entire string following the /k/th match
925--
923--
926-- Examples:
924-- Examples:
927--
925--
928-- > find "::" ""
926-- > find "::" ""
929-- > ==> []
927-- > ==> []
930-- > find "/" "a/b/c/"
928-- > find "/" "a/b/c/"
931−-- > ==> [("a", "/", "b/c/"), ("a/b", "/", "c/"), ("a/b/c", "/", "")]
929+-- > ==> [("a", "/b/c/"), ("a/b", "/c/"), ("a/b/c", "/")]
932--
930--
933-- This function is strict in its first argument, and lazy in its
931-- This function is strict in its first argument, and lazy in its
934-- second.
932-- second.
935--
933--
936-- In (unlikely) bad cases, this function's time complexity degrades
934-- In (unlikely) bad cases, this function's time complexity degrades
937-- towards /O(n*m)/.
935-- towards /O(n*m)/.
936+--
937+-- The @needle@ parameter may not be empty.
938+find :: Text -- ^ @needle@ to search for
939+ -> Text -- ^ @haystack@ in which to search
938−find :: Text -> Text -> [(Text, Text, Text)]
940+ -> [(Text, Text)]
939find pat src
941find pat src
940 | null pat = emptyError "find"
942 | null pat = emptyError "find"
941 | otherwise = L.map step (indices pat src)
943 | otherwise = L.map step (indices pat src)
942 where
944 where
943− step x = (take x src, pat, drop (x + l) pat)
945+ step x = let h :*: t = splitAtWord x src
944− l = length pat
946+ in (h, t)
945
947
946-- | /O(n)/ 'breakBy' is like 'spanBy', but the prefix returned is over
948-- | /O(n)/ 'breakBy' is like 'spanBy', but the prefix returned is over
947-- elements that fail the predicate @p@.
949-- elements that fail the predicate @p@.
Data/Text/Search.hs+2−1
@@ -40,7 +40,8 @@ import Data.Bits ((.|.), (.&.))
40import Data.Text.UnsafeShift (shiftL)
40import Data.Text.UnsafeShift (shiftL)
41
41
42-- | /O(n+m)/ Find the offsets of all non-overlapping indices of
42-- | /O(n+m)/ Find the offsets of all non-overlapping indices of
43−-- @needle@ within @haystack@.
43+-- @needle@ within @haystack@. The offsets returned represent
44+-- locations in the low-level array.
44--
45--
45-- In (unlikely) bad cases, this algorithm's complexity degrades
46-- In (unlikely) bad cases, this algorithm's complexity degrades
46-- towards /O(n*m)/.
47-- towards /O(n*m)/.
tests/Properties.hs+10−6
@@ -408,11 +408,13 @@ t_inits = L.inits `eqP` (map unpackS . T.inits)
408tl_inits = L.inits `eqP` (map unpackS . TL.inits)
408tl_inits = L.inits `eqP` (map unpackS . TL.inits)
409t_tails = L.tails `eqP` (map unpackS . T.tails)
409t_tails = L.tails `eqP` (map unpackS . T.tails)
410tl_tails = L.tails `eqP` (map unpackS . TL.tails)
410tl_tails = L.tails `eqP` (map unpackS . TL.tails)
411−t_findConcat (NotEmpty s) t = all (==t) $ map conc (T.find s t)
411+t_findAppendId (NotEmpty s) t = all (==t) $ map (uncurry T.append) (T.find s t)
412− where conc (a,b,c) = T.concat [a,b,c]
412+tl_findAppendId (NotEmpty s) t = all (==t) $ map (uncurry TL.append) (TL.find s t)
413+t_findContains (NotEmpty s) = unsquare (all (T.isPrefixOf s . snd) . T.find s .
414+ T.intercalate s)
415+tl_findContains (NotEmpty s) = unsquare (all (TL.isPrefixOf s . snd) .
416+ TL.find s . TL.intercalate s)
413t_findCount s = (L.length . T.find s) `eq` T.count s
417t_findCount s = (L.length . T.find s) `eq` T.count s
414−tl_findConcat (NotEmpty s) t = all (==t) $ map conc (TL.find s t)
415− where conc (a,b,c) = TL.concat [a,b,c]
416tl_findCount s = (L.genericLength . TL.find s) `eq` TL.count s
418tl_findCount s = (L.genericLength . TL.find s) `eq` TL.count s
417
419
418t_split_split s = unsquare ((T.split s `eq` Slow.split s) .
420t_split_split s = unsquare ((T.split s `eq` Slow.split s) .
@@ -810,9 +812,11 @@ tests = [
810 ],
812 ],
811
813
812 testGroup "breaking many" [
814 testGroup "breaking many" [
813− testProperty "t_findConcat" t_findConcat,
815+ testProperty "t_findAppendId" t_findAppendId,
816+ testProperty "tl_findAppendId" tl_findAppendId,
817+ testProperty "t_findContains" t_findContains,
818+ testProperty "tl_findContains" tl_findContains,
814 testProperty "t_findCount" t_findCount,
819 testProperty "t_findCount" t_findCount,
815− testProperty "tl_findConcat" tl_findConcat,
816 testProperty "tl_findCount" tl_findCount,
820 testProperty "tl_findCount" tl_findCount,
817 testProperty "t_split_split" t_split_split,
821 testProperty "t_split_split" t_split_split,
818 testProperty "tl_split_split" tl_split_split,
822 testProperty "tl_split_split" tl_split_split,