on master help sign in
Fix up design and implementation of find

--HG-- extra : convert_revision : 103b77f2ae6513c5198b248519e6a072133da262

commit92d93c38f08d62a594391697dd38dcd4101fcea1 authorBryan O'Sullivan <bos@serpentine.com> dateFri 23 Jul 2010, 19:32 (16 years ago) parentefd3ab4
4 files changed+33−25
Data/Text.hs+10−9 Data/Text/Lazy.hs+11−9 Data/Text/Search.hs+2−1 tests/Properties.hs+10−6
Data/Text.hs+10−9
@@ -1086,30 +1086,31 @@ breakEnd pat src = let (a,b) = break (reverse pat) (reverse src)
10861086{-# INLINE breakEnd #-}
10871087
10881088-- | /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:
10901090--
1091−-- * The entire string prior to the /k/th match
1091+-- * The entire string prior to the /k/th match (i.e. the prefix)
10921092--
1093−-- * The /k/th match
1094−--
1095−-- * The entire string following the /k/th match
1093+-- * The /k/th match, followed by the remainder of the string
10961094--
10971095-- Examples:
10981096--
10991097-- > find "::" ""
11001098-- > ==> []
11011099-- > find "/" "a/b/c/"
1102−-- > ==> [("a", "/", "b/c/"), ("a/b", "/", "c/"), ("a/b/c", "/", "")]
1100+-- > ==> [("a", "/b/c/"), ("a/b", "/c/"), ("a/b/c", "/")]
11031101--
11041102-- In (unlikely) bad cases, this function's time complexity degrades
11051103-- towards /O(n*m)/.
1106−find :: Text -> Text -> [(Text, Text, Text)]
1104+--
1105+-- The @needle@ parameter may not be empty.
1106+find :: Text -- ^ @needle@ to search for
1107+ -> Text -- ^ @haystack@ in which to search
1108+ -> [(Text, Text)]
11071109find pat@(Text _ _ plen) src@(Text arr off slen)
11081110 | null pat = emptyError "find"
11091111 | otherwise = L.map step (indices pat src)
11101112 where
1111− step x = (chunk 0 x, chunk x plen, chunk (x+plen) (stop-x))
1112− stop = slen - plen
1113+ step x = (chunk 0 x, chunk x (slen-x))
11131114 chunk !n !l = textP arr (n+off) l
11141115{-# INLINE find #-}
11151116
Data/Text/Lazy.hs+11−9
@@ -915,33 +915,35 @@ breakEnd pat src = let (a,b) = break (reverse pat) (reverse src)
915915{-# INLINE breakEnd #-}
916916
917917-- | /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:
919919--
920−-- * The entire string prior to the /k/th match
920+-- * The entire string prior to the /k/th match (i.e. the prefix)
921921--
922−-- * The /k/th match
923−--
924−-- * The entire string following the /k/th match
922+-- * The /k/th match, followed by the remainder of the string
925923--
926924-- Examples:
927925--
928926-- > find "::" ""
929927-- > ==> []
930928-- > find "/" "a/b/c/"
931−-- > ==> [("a", "/", "b/c/"), ("a/b", "/", "c/"), ("a/b/c", "/", "")]
929+-- > ==> [("a", "/b/c/"), ("a/b", "/c/"), ("a/b/c", "/")]
932930--
933931-- This function is strict in its first argument, and lazy in its
934932-- second.
935933--
936934-- In (unlikely) bad cases, this function's time complexity degrades
937935-- towards /O(n*m)/.
938−find :: Text -> Text -> [(Text, Text, Text)]
936+--
937+-- The @needle@ parameter may not be empty.
938+find :: Text -- ^ @needle@ to search for
939+ -> Text -- ^ @haystack@ in which to search
940+ -> [(Text, Text)]
939941find pat src
940942 | null pat = emptyError "find"
941943 | otherwise = L.map step (indices pat src)
942944 where
943− step x = (take x src, pat, drop (x + l) pat)
944− l = length pat
945+ step x = let h :*: t = splitAtWord x src
946+ in (h, t)
945947
946948-- | /O(n)/ 'breakBy' is like 'spanBy', but the prefix returned is over
947949-- elements that fail the predicate @p@.
Data/Text/Search.hs+2−1
@@ -40,7 +40,8 @@ import Data.Bits ((.|.), (.&.))
4040import Data.Text.UnsafeShift (shiftL)
4141
4242-- | /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.
4445--
4546-- In (unlikely) bad cases, this algorithm's complexity degrades
4647-- towards /O(n*m)/.
tests/Properties.hs+10−6
@@ -408,11 +408,13 @@ t_inits = L.inits `eqP` (map unpackS . T.inits)
408408tl_inits = L.inits `eqP` (map unpackS . TL.inits)
409409t_tails = L.tails `eqP` (map unpackS . T.tails)
410410tl_tails = L.tails `eqP` (map unpackS . TL.tails)
411−t_findConcat (NotEmpty s) t = all (==t) $ map conc (T.find s t)
412− where conc (a,b,c) = T.concat [a,b,c]
411+t_findAppendId (NotEmpty s) t = all (==t) $ map (uncurry T.append) (T.find s t)
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)
413417t_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]
416418tl_findCount s = (L.genericLength . TL.find s) `eq` TL.count s
417419
418420t_split_split s = unsquare ((T.split s `eq` Slow.split s) .
@@ -810,9 +812,11 @@ tests = [
810812 ],
811813
812814 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,
814819 testProperty "t_findCount" t_findCount,
815− testProperty "tl_findConcat" tl_findConcat,
816820 testProperty "tl_findCount" tl_findCount,
817821 testProperty "t_split_split" t_split_split,
818822 testProperty "tl_split_split" tl_split_split,