on master help sign in
Implement the other API enhancements in the Lazy module.

--HG-- extra : convert_revision : d935ae018a3a94c0134e744f659ced1ccda9b2c0

commit9858770da04c4d5ce76e99a3e8e972fae59efef3 authorBryan O'Sullivan <bos@serpentine.com> dateSun 16 Aug 2009, 04:21 (17 years ago) parentdf4e5e8
2 files changed+163−18
Data/Text/Lazy.hs+149−16 tests/Properties.hs+14−2
Data/Text/Lazy.hs+149−16
@@ -1,4 +1,5 @@
11{-# OPTIONS_GHC -fno-warn-orphans #-}
2+{-# LANGUAGE BangPatterns #-}
23-- |
34-- Module : Data.Text.Lazy
45-- Copyright : (c) Bryan O'Sullivan 2009
@@ -57,6 +58,7 @@ module Data.Text.Lazy
5758 , intersperse
5859 , transpose
5960 , reverse
61+ , replace
6062
6163 -- ** Case conversion
6264 -- $case
@@ -109,6 +111,11 @@ module Data.Text.Lazy
109111 , drop
110112 , takeWhile
111113 , dropWhile
114+ , dropWhileEnd
115+ , dropAround
116+ , strip
117+ , stripStart
118+ , stripEnd
112119 , splitAt
113120 , span
114121 , break
@@ -118,7 +125,10 @@ module Data.Text.Lazy
118125 , tails
119126
120127 -- ** Breaking into many substrings
128+ -- $split
121129 , split
130+ , splitTimes
131+ , splitTimesEnd
122132 , splitWith
123133 , chunksOf
124134 -- , breakSubstring
@@ -160,7 +170,7 @@ module Data.Text.Lazy
160170
161171import Prelude (Char, Bool(..), Int, Maybe(..), String,
162172 Eq(..), Ord(..), Read(..), Show(..),
163− (&&), (+), (-), (.), ($), (++),
173+ (&&), (||), (+), (-), (.), ($), (++),
164174 div, flip, fromIntegral, not, otherwise)
165175import qualified Prelude as P
166176import Data.Int (Int64)
@@ -430,6 +440,14 @@ reverse = rev Empty
430440 where rev a Empty = a
431441 rev a (Chunk t ts) = rev (Chunk (T.reverse t) a) ts
432442
443+-- | /O(m)*O(n)/ Replace every occurrence of one substring with another.
444+replace :: Text -- ^ Text to search for
445+ -> Text -- ^ Replacement text
446+ -> Text -- ^ Input text
447+ -> Text
448+replace s d = intercalate d . split s
449+{-# INLINE replace #-}
450+
433451-- ----------------------------------------------------------------------------
434452-- ** Case conversions (folds)
435453
@@ -662,7 +680,7 @@ take i t0 = take' i t0
662680-- | /O(n)/ 'drop' @n@, applied to a 'Text', returns the suffix of the
663681-- 'Text' of length @n@, or the empty 'Text' if @n@ is greater than the
664682-- length of the 'Text'. Subject to fusion.
665−drop :: Int -> Text -> Text
683+drop :: Int64 -> Text -> Text
666684drop i t0
667685 | i <= 0 = t0
668686 | otherwise = drop' i t0
@@ -718,6 +736,52 @@ dropWhile p t0 = dropWhile' t0
718736"LAZY TEXT dropWhile -> unfused" [1] forall p t.
719737 unstream (S.dropWhile p (stream t)) = dropWhile p t
720738 #-}
739+-- | /O(n)/ 'dropWhileEnd' @p@ @t@ returns the prefix remaining after
740+-- dropping characters that fail the predicate @p@ from the end of
741+-- @t@.
742+-- Examples:
743+--
744+-- > dropWhileEnd (=='.') "foo..." == "foo"
745+dropWhileEnd :: (Char -> Bool) -> Text -> Text
746+dropWhileEnd p = go
747+ where go Empty = Empty
748+ go (Chunk t Empty) = if T.null t'
749+ then Empty
750+ else Chunk t' Empty
751+ where t' = T.dropWhileEnd p t
752+ go (Chunk t ts) = case go ts of
753+ Empty -> go (Chunk t Empty)
754+ ts' -> Chunk t ts'
755+{-# INLINE dropWhileEnd #-}
756+
757+-- | /O(n)/ 'dropAround' @p@ @t@ returns the substring remaining after
758+-- dropping characters that fail the predicate @p@ from both the
759+-- beginning and end of @t@. Subject to fusion.
760+dropAround :: (Char -> Bool) -> Text -> Text
761+dropAround p = dropWhile p . dropWhileEnd p
762+{-# INLINE [1] dropAround #-}
763+
764+-- | /O(n)/ Remove leading white space from a string. Equivalent to:
765+--
766+-- > dropWhile isSpace
767+stripStart :: Text -> Text
768+stripStart = dropWhile isSpace
769+{-# INLINE [1] stripStart #-}
770+
771+-- | /O(n)/ Remove trailing white space from a string. Equivalent to:
772+--
773+-- > dropWhileEnd isSpace
774+stripEnd :: Text -> Text
775+stripEnd = dropWhileEnd isSpace
776+{-# INLINE [1] stripEnd #-}
777+
778+-- | /O(n)/ Remove leading and trailing white space from a string.
779+-- Equivalent to:
780+--
781+-- > dropAround isSpace
782+strip :: Text -> Text
783+strip = dropAround isSpace
784+{-# INLINE [1] strip #-}
721785
722786-- | /O(n)/ 'splitAt' @n t@ returns a pair whose first element is a
723787-- prefix of @t@ of length @n@, and whose second is the remainder of
@@ -791,24 +855,93 @@ tails ts@(Chunk t ts')
791855 | T.length t == 1 = ts : tails ts'
792856 | otherwise = ts : tails (Chunk (T.unsafeTail t) ts')
793857
794−-- | /O(n)/ Break a 'Text' into pieces separated by the byte
795−-- argument, consuming the delimiter. I.e.
858+-- $split
859+--
860+-- Splitting functions in this library do not perform character-wise
861+-- copies to create substrings; they just construct new 'Text's that
862+-- are slices of the original.
863+
864+-- | /O(m)*O(n)/ Break a 'Text' into pieces separated by the first
865+-- 'Text' argument, consuming the delimiter. Examples:
796866--
797−-- > split '\n' "a\nb\nd\ne" == ["a","b","d","e"]
798−-- > split 'a' "aXaXaXa" == ["","X","X","X",""]
799−-- > split 'x' "x" == ["",""]
867+-- > split "\r\n" "a\r\nb\r\nd\r\ne" == ["a","b","d","e"]
868+-- > split "aaa" "aaaXaaaXaaaXaaa" == ["","X","X","X",""]
869+-- > split "x" "x" == ["",""]
800870--
801871-- and
802872--
803−-- > intercalate [c] . split c == id
804−-- > split == splitWith . (==)
805−--
806−-- As for all splitting functions in this library, this function does
807−-- not copy the substrings, it just constructs new 'Text's that are
808−-- slices of the original.
809−split :: Char -> Text -> [Text]
810−split c = splitWith (==c)
811−{-# INLINE split #-}
873+-- > intercalate s . split s == id
874+-- > split (singleton c) == splitWith (==c)
875+split :: Text -- ^ Text to split on
876+ -> Text -- ^ Input text
877+ -> [Text]
878+split pat src0
879+ | l == 0 = [src0]
880+ | l == 1 = splitWith (== (head pat)) src0
881+ | otherwise = go src0
882+ where
883+ l = length pat
884+ go src = search 0 src
885+ where
886+ search !n !s
887+ | null s = [src] -- not found
888+ | pat `isPrefixOf` s = take n src : go (drop l s)
889+ | otherwise = search (n+1) (tail s)
890+{-# INLINE [1] split #-}
891+
892+{-# RULES
893+"LAZY TEXT split/singleton -> splitWith/==" [~1] forall c t.
894+ split (singleton c) t = splitWith (==c) t
895+ #-}
896+
897+-- | /O(m)*O(n)/ Break a 'Text' into pieces at most @k@ times,
898+-- treating the first 'Text' argument as the delimiter to break on,
899+-- and consuming the delimiter. The last element of the list contains
900+-- the remaining text after the number of times to split has been
901+-- reached. A value of zero or less for @k@ causes no splitting to
902+-- occur.
903+--
904+-- Examples:
905+--
906+-- > splitTimes 0 "//" "a//b//c" == ["a//b//c"]
907+-- > splitTimes 2 ":" "a:b:c:d:e" == ["a","b","c:d:e"]
908+-- > splitTimes 100 "???" "a????b" == ["a","?b"]
909+--
910+-- and
911+--
912+-- > intercalate s . splitTimes k s == id
913+splitTimes :: Int64 -- ^ Maximum number of times to split
914+ -> Text -- ^ Text to split on
915+ -> Text -- ^ Input text
916+ -> [Text]
917+splitTimes k pat src0
918+ | k <= 0 || l == 0 = [src0]
919+ | otherwise = go k src0
920+ where
921+ l = length pat
922+ go !i src = search 0 src
923+ where
924+ search !n !s
925+ | i == 0 || null s = [src] -- not found or limit reached
926+ | pat `isPrefixOf` s = take n src : go (i-1) (drop l s)
927+ | otherwise = search (n+1) (tail s)
928+{-# INLINE splitTimes #-}
929+
930+-- | /O(m)*O(n)/ Break a 'Text' into pieces at most @k@ times, like
931+-- 'splitTimes', but start from the end of the input and work towards
932+-- the start.
933+--
934+-- Examples:
935+--
936+-- > splitTimes 2 "::" "a::b::c::d::e" == ["a","b","c::d::e"]
937+-- > splitTimesEnd 2 "::" "a::b::c::d::e" == ["a::b::c","d","e"]
938+splitTimesEnd :: Int64 -- ^ Maximum number of times to split
939+ -> Text -- ^ Text to split on
940+ -> Text -- ^ Input text
941+ -> [Text]
942+splitTimesEnd k pat src =
943+ L.reverse . L.map reverse $ splitTimes k (reverse pat) (reverse src)
944+{-# INLINE splitTimesEnd #-}
812945
813946-- | /O(n)/ Splits a 'Text' into components delimited by separators,
814947-- where the predicate returns True for a separator element. The
tests/Properties.hs+14−2
@@ -205,6 +205,7 @@ tl_reverse = L.reverse `eqP` (unpackS . TL.reverse)
205205t_reverse_short n = L.reverse `eqP` (unpackS . S.reverse . shorten n . S.stream)
206206
207207t_replace s d = (L.intercalate d . split s) `eqP` (unpackS . T.replace (T.pack s) (T.pack d))
208+tl_replace s d = (L.intercalate d . split s) `eqP` (unpackS . TL.replace (TL.pack s) (TL.pack d))
208209
209210split :: (Eq a) => [a] -> [a] -> [[a]]
210211split pat src0
@@ -333,7 +334,7 @@ tl_take n = L.take n `eqP` (unpackS . TL.take (fromIntegral n))
333334s_drop n = L.drop n `eqP` (unpackS . S.drop n)
334335sf_drop p n = (L.drop n . L.filter p) `eqP` (unpackS . S.drop n . S.filter p)
335336t_drop n = L.drop n `eqP` (unpackS . T.drop n)
336−tl_drop n = L.drop n `eqP` (unpackS . TL.drop n)
337+tl_drop n = L.drop n `eqP` (unpackS . TL.drop (fromIntegral n))
337338s_takeWhile p = L.takeWhile p `eqP` (unpackS . S.takeWhile p)
338339sf_takeWhile q p = (L.takeWhile p . L.filter q) `eqP` (unpackS . S.takeWhile p . S.filter q)
339340t_takeWhile p = L.takeWhile p `eqP` (unpackS . T.takeWhile p)
@@ -343,10 +344,15 @@ sf_dropWhile q p = (L.dropWhile p . L.filter q) `eqP` (unpackS . S.dropWhile p
343344t_dropWhile p = L.dropWhile p `eqP` (unpackS . T.dropWhile p)
344345tl_dropWhile p = L.dropWhile p `eqP` (unpackS . S.dropWhile p)
345346t_dropWhileEnd p = (L.reverse . L.dropWhile p . L.reverse) `eqP` (unpackS . T.dropWhileEnd p)
347+tl_dropWhileEnd p = (L.reverse . L.dropWhile p . L.reverse) `eqP` (unpackS . TL.dropWhileEnd p)
346348t_dropAround p = (L.dropWhile p . L.reverse . L.dropWhile p . L.reverse) `eqP` (unpackS . T.dropAround p)
349+tl_dropAround p = (L.dropWhile p . L.reverse . L.dropWhile p . L.reverse) `eqP` (unpackS . TL.dropAround p)
347350t_stripStart = T.dropWhile isSpace `eq` T.stripStart
351+tl_stripStart = TL.dropWhile isSpace `eq` TL.stripStart
348352t_stripEnd = T.dropWhileEnd isSpace `eq` T.stripEnd
353+tl_stripEnd = TL.dropWhileEnd isSpace `eq` TL.stripEnd
349354t_strip = T.dropAround isSpace `eq` T.strip
355+tl_strip = TL.dropAround isSpace `eq` TL.strip
350356t_splitAt n = L.splitAt n `eqP` (unpack2 . T.splitAt n)
351357tl_splitAt n = L.splitAt n `eqP` (unpack2 . TL.splitAt (fromIntegral n))
352358t_span p = L.span p `eqP` (unpack2 . T.span p)
@@ -370,7 +376,7 @@ t_splitTimes_split k t = T.splitTimes k t `eq` \u ->
370376 (a,b) -> a ++ [T.intercalate t b]
371377t_splitTimesEnd_i k t = id `eq` (T.intercalate t . T.splitTimesEnd k t)
372378t_splitTimesEnd_split t = T.splitTimesEnd maxBound t `eq` T.split t
373−tl_split_i c = id `eq` (TL.intercalate (TL.singleton c) . TL.split c)
379+tl_split_i t = id `eq` (TL.intercalate t . TL.split t)
374380
375381t_splitWith p = splitWith p `eqP` (map unpackS . T.splitWith p)
376382t_splitWith_count c = (L.length . T.splitWith (==c)) `eq` ((1+) . T.count c)
@@ -603,6 +609,7 @@ tests = [
603609 testProperty "tl_reverse" tl_reverse,
604610 testProperty "t_reverse_short" t_reverse_short,
605611 testProperty "t_replace" t_replace,
612+ testProperty "tl_replace" tl_replace,
606613
607614 testGroup "case conversion" [
608615 testProperty "s_toCaseFold_length" s_toCaseFold_length,
@@ -715,10 +722,15 @@ tests = [
715722 testProperty "t_dropWhile" t_dropWhile,
716723 testProperty "tl_dropWhile" tl_dropWhile,
717724 testProperty "t_dropWhileEnd" t_dropWhileEnd,
725+ testProperty "tl_dropWhileEnd" tl_dropWhileEnd,
718726 testProperty "t_dropAround" t_dropAround,
727+ testProperty "tl_dropAround" tl_dropAround,
719728 testProperty "t_stripStart" t_stripStart,
729+ testProperty "tl_stripStart" tl_stripStart,
720730 testProperty "t_stripEnd" t_stripEnd,
731+ testProperty "tl_stripEnd" tl_stripEnd,
721732 testProperty "t_strip" t_strip,
733+ testProperty "tl_strip" tl_strip,
722734 testProperty "t_splitAt" t_splitAt,
723735 testProperty "tl_splitAt" tl_splitAt,
724736 testProperty "t_span" t_span,