Implement takeWhileEnd
authorPaul Capron <paul@fragara.com>
Wed, 7 Oct 2015 22:18:11 +0000 (00:18 +0200)
committerPaul Capron <paul@fragara.com>
Wed, 7 Oct 2015 22:18:11 +0000 (00:18 +0200)
This commit adds a takeWhileEnd function to both Data.Text and Data.Text.Lazy,
and related QuickCheck properties.

takeWhileEnd, applied to a predicate `p' and a Text `t', returns the longest
suffix (possibly empty) of elements in `t' that satisfy `p'.

Data/Text.hs
Data/Text/Lazy.hs
tests/Tests/Properties.hs

index c38bed9..9a2d31e 100644 (file)
@@ -129,6 +129,7 @@ module Data.Text
     , drop
     , dropEnd
     , takeWhile
+    , takeWhileEnd
     , dropWhile
     , dropWhileEnd
     , dropAround
@@ -1124,6 +1125,27 @@ takeWhile p t@(Text arr off len) = loop 0
     unstream (S.takeWhile p (stream t)) = takeWhile p t
   #-}
 
+-- | /O(n)/ 'takeWhileEnd', applied to a predicate @p@ and a 'Text',
+-- returns the longest suffix (possibly empty) of elements that
+-- satisfy @p@.  Subject to fusion.
+-- Examples:
+--
+-- > takeWhileEnd (=='o') "foo" == "oo"
+takeWhileEnd :: (Char -> Bool) -> Text -> Text
+takeWhileEnd p t@(Text arr off len) = loop (len-1) len
+  where loop !i !l | l <= 0    = t
+                   | p c       = loop (i+d) (l+d)
+                   | otherwise = text arr (off+l) (len-l)
+            where (c,d)        = reverseIter t i
+{-# INLINE [1] takeWhileEnd #-}
+
+{-# RULES
+"TEXT takeWhileEnd -> fused" [~1] forall p t.
+    takeWhileEnd p t = S.reverse (S.takeWhile p (S.reverseStream t))
+"TEXT takeWhileEnd -> unfused" [1] forall p t.
+    S.reverse (S.takeWhile p (S.reverseStream t)) = takeWhileEnd p t
+  #-}
+
 -- | /O(n)/ 'dropWhile' @p@ @t@ returns the suffix remaining after
 -- 'takeWhile' @p@ @t@. Subject to fusion.
 dropWhile :: (Char -> Bool) -> Text -> Text
index c03734e..2371dfa 100644 (file)
@@ -140,6 +140,7 @@ module Data.Text.Lazy
     , drop
     , dropEnd
     , takeWhile
+    , takeWhileEnd
     , dropWhile
     , dropWhileEnd
     , dropAround
@@ -1119,6 +1120,20 @@ takeWhile p t0 = takeWhile' t0
 "LAZY TEXT takeWhile -> unfused" [1] forall p t.
     unstream (S.takeWhile p (stream t)) = takeWhile p t
   #-}
+-- | /O(n)/ 'takeWhileEnd', applied to a predicate @p@ and a 'Text',
+-- returns the longest suffix (possibly empty) of elements that
+-- satisfy @p@.
+-- Examples:
+--
+-- > takeWhileEnd (=='o') "foo" == "oo"
+takeWhileEnd :: (Char -> Bool) -> Text -> Text
+takeWhileEnd p = takeChunk empty . L.reverse . toChunks
+  where takeChunk acc []     = acc
+        takeChunk acc (t:ts) = if T.length t' < T.length t
+                               then (Chunk t' acc)
+                               else takeChunk (Chunk t' acc) ts
+          where t' = T.takeWhileEnd p t
+{-# INLINE takeWhileEnd #-}
 
 -- | /O(n)/ 'dropWhile' @p@ @t@ returns the suffix remaining after
 -- 'takeWhile' @p@ @t@.  Subject to fusion.
index 94e0c8a..90450ff 100644 (file)
@@ -516,6 +516,10 @@ sf_takeWhile q p  = (L.takeWhile p . L.filter q) `eqP`
                     (unpackS . S.takeWhile p . S.filter q)
 t_takeWhile p     = L.takeWhile p `eqP` (unpackS . T.takeWhile p)
 tl_takeWhile p    = L.takeWhile p `eqP` (unpackS . TL.takeWhile p)
+t_takeWhileEnd p  = (L.reverse . L.takeWhile p . L.reverse) `eqP`
+                    (unpackS . T.takeWhileEnd p)
+tl_takeWhileEnd p = (L.reverse . L.takeWhile p . L.reverse) `eqP`
+                    (unpackS . TL.takeWhileEnd p)
 s_dropWhile p     = L.dropWhile p `eqP` (unpackS . S.dropWhile p)
 s_dropWhile_s p   = L.dropWhile p `eqP` (unpackS . S.unstream . S.dropWhile p)
 sf_dropWhile q p  = (L.dropWhile p . L.filter q) `eqP`
@@ -1142,6 +1146,8 @@ tests =
         testProperty "sf_takeWhile" sf_takeWhile,
         testProperty "t_takeWhile" t_takeWhile,
         testProperty "tl_takeWhile" tl_takeWhile,
+        testProperty "t_takeWhileEnd" t_takeWhileEnd,
+        testProperty "tl_takeWhileEnd" tl_takeWhileEnd,
         testProperty "sf_dropWhile" sf_dropWhile,
         testProperty "s_dropWhile" s_dropWhile,
         testProperty "s_dropWhile_s" s_dropWhile_s,