Skip to content

Commit 38625d5

Browse files
committed
Implement folds directly, without resorting to streaming framework
1 parent acf18b6 commit 38625d5

File tree

1 file changed

+48
-15
lines changed

1 file changed

+48
-15
lines changed

src/Data/Text.hs

Lines changed: 48 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1053,30 +1053,46 @@ transpose ts = P.map pack (L.transpose (P.map unpack ts))
10531053
-- (typically the left-identity of the operator), and a 'Text',
10541054
-- reduces the 'Text' using the binary operator, from left to right.
10551055
foldl :: (a -> Char -> a) -> a -> Text -> a
1056-
foldl f z t = S.foldl f z (stream t)
1056+
foldl f z (Text arr off len) = go (off + len - 1)
1057+
where
1058+
go !i
1059+
| i < off = z
1060+
| otherwise = let !(Iter c l) = reverseIterArray arr i in f (go (i + l)) c
10571061
{-# INLINE foldl #-}
10581062

10591063
-- | /O(n)/ A strict version of 'foldl'.
10601064
foldl' :: (a -> Char -> a) -> a -> Text -> a
1061-
foldl' f z t = S.foldl' f z (stream t)
1065+
foldl' f z (Text arr off len) = go off z
1066+
where
1067+
go !i !acc
1068+
| i >= off + len = acc
1069+
| otherwise = let !(Iter c l) = iterArray arr i in go (i + l) (f acc c)
10621070
{-# INLINE foldl' #-}
10631071

10641072
-- | /O(n)/ A variant of 'foldl' that has no starting value argument,
10651073
-- and thus must be applied to a non-empty 'Text'.
10661074
foldl1 :: HasCallStack => (Char -> Char -> Char) -> Text -> Char
1067-
foldl1 f t = S.foldl1 f (stream t)
1075+
foldl1 f t = case uncons t of
1076+
Nothing -> emptyError "foldl"
1077+
Just (c, t') -> foldl f c t'
10681078
{-# INLINE foldl1 #-}
10691079

10701080
-- | /O(n)/ A strict version of 'foldl1'.
10711081
foldl1' :: HasCallStack => (Char -> Char -> Char) -> Text -> Char
1072-
foldl1' f t = S.foldl1' f (stream t)
1082+
foldl1' f t = case uncons t of
1083+
Nothing -> emptyError "foldl'"
1084+
Just (c, t') -> foldl' f c t'
10731085
{-# INLINE foldl1' #-}
10741086

10751087
-- | /O(n)/ A monadic version of 'foldl''.
10761088
--
10771089
-- @since 2.1.2
10781090
foldlM' :: Monad m => (a -> Char -> m a) -> a -> Text -> m a
1079-
foldlM' f z t = S.foldlM' f z (stream t)
1091+
foldlM' f z (Text arr off len) = go off z
1092+
where
1093+
go !i !acc
1094+
| i >= off + len = pure acc
1095+
| otherwise = let !(Iter c l) = iterArray arr i in go (i + l) P.=<< f acc c
10801096
{-# INLINE foldlM' #-}
10811097

10821098
-- | /O(n)/ 'foldr', applied to a binary operator, a starting value
@@ -1099,13 +1115,19 @@ foldlM' f z t = S.foldlM' f z (stream t)
10991115
-- Searches from left to right with short-circuiting behavior can
11001116
-- also be defined using 'foldr' (/e.g./, 'any', 'all', 'find', 'elem').
11011117
foldr :: (Char -> a -> a) -> a -> Text -> a
1102-
foldr f z t = S.foldr f z (stream t)
1118+
foldr f z (Text arr off len) = go off
1119+
where
1120+
go !i
1121+
| i >= off + len = z
1122+
| otherwise = let !(Iter c l) = iterArray arr i in f c (go (i + l))
11031123
{-# INLINE foldr #-}
11041124

11051125
-- | /O(n)/ A variant of 'foldr' that has no starting value argument,
11061126
-- and thus must be applied to a non-empty 'Text'.
11071127
foldr1 :: HasCallStack => (Char -> Char -> Char) -> Text -> Char
1108-
foldr1 f t = S.foldr1 f (stream t)
1128+
foldr1 f t = case unsnoc t of
1129+
Nothing -> emptyError "foldr1"
1130+
Just (t', c) -> foldr f c t'
11091131
{-# INLINE foldr1 #-}
11101132

11111133
-- | /O(n)/ A strict version of 'foldr'.
@@ -1114,7 +1136,11 @@ foldr1 f t = S.foldr1 f (stream t)
11141136
--
11151137
-- @since 2.0.1
11161138
foldr' :: (Char -> a -> a) -> a -> Text -> a
1117-
foldr' f z t = S.foldl' (P.flip f) z (reverseStream t)
1139+
foldr' f z (Text arr off len) = go (off + len - 1) z
1140+
where
1141+
go !i !acc
1142+
| i < off = acc
1143+
| otherwise = let !(Iter c l) = reverseIterArray arr i in go (i + l) (f c acc)
11181144
{-# INLINE foldr' #-}
11191145

11201146
-- -----------------------------------------------------------------------------
@@ -1144,25 +1170,30 @@ concatMap f = concat . foldr ((:) . f) []
11441170
-- | /O(n)/ 'any' @p@ @t@ determines whether any character in the
11451171
-- 'Text' @t@ satisfies the predicate @p@.
11461172
any :: (Char -> Bool) -> Text -> Bool
1147-
any p t = S.any p (stream t)
1173+
any p = foldr (\c acc -> p c || acc) False
11481174
{-# INLINE any #-}
11491175

11501176
-- | /O(n)/ 'all' @p@ @t@ determines whether all characters in the
11511177
-- 'Text' @t@ satisfy the predicate @p@.
11521178
all :: (Char -> Bool) -> Text -> Bool
1153-
all p t = S.all p (stream t)
1179+
all p = foldr (\c acc -> p c && acc) True
11541180
{-# INLINE all #-}
11551181

11561182
-- | /O(n)/ 'maximum' returns the maximum value from a 'Text', which
11571183
-- must be non-empty.
11581184
maximum :: HasCallStack => Text -> Char
1159-
maximum t = S.maximum (stream t)
1185+
maximum = foldl1' max
1186+
-- This could be implemented faster: look for the longest
1187+
-- and largest UTF-8 sequence, then decode it to Char only once,
1188+
-- instead of decoding all characters, but I doubt anyone cares
1189+
-- about the performance of 'maximum' much.
11601190
{-# INLINE maximum #-}
11611191

11621192
-- | /O(n)/ 'minimum' returns the minimum value from a 'Text', which
11631193
-- must be non-empty.
11641194
minimum :: HasCallStack => Text -> Char
1165-
minimum t = S.minimum (stream t)
1195+
minimum = foldl1' min
1196+
-- This could be implemented faster, see the comment for 'maximum' above.
11661197
{-# INLINE minimum #-}
11671198

11681199
-- -----------------------------------------------------------------------------
@@ -1735,14 +1766,16 @@ chunksOf k = go
17351766
-- returns 'True' if the element is found in the given 'Text', or
17361767
-- 'False' otherwise.
17371768
elem :: Char -> Text -> Bool
1738-
elem c t = S.any (== c) (stream t)
1769+
elem = any . (==)
1770+
-- TODO This can be implemented much faster: there is no need to decode
1771+
-- any UTF-8 sequences at all.
17391772
{-# INLINE elem #-}
17401773

17411774
-- | /O(n)/ The 'find' function takes a predicate and a 'Text', and
17421775
-- returns the first element matching the predicate, or 'Nothing' if
17431776
-- there is no such element.
17441777
find :: (Char -> Bool) -> Text -> Maybe Char
1745-
find p t = S.findBy p (stream t)
1778+
find p = foldr (\c acc -> if p c then Just c else acc) Nothing
17461779
{-# INLINE find #-}
17471780

17481781
-- | /O(n)/ The 'partition' function takes a predicate and a 'Text',
@@ -1903,7 +1936,7 @@ count pat
19031936
-- | /O(n)/ The 'countChar' function returns the number of times the
19041937
-- query element appears in the given 'Text'.
19051938
countChar :: Char -> Text -> Int
1906-
countChar c t = S.countChar c (stream t)
1939+
countChar c = foldl' (\acc c' -> if c == c' then acc + 1 else acc) 0
19071940
{-# INLINE countChar #-}
19081941

19091942
-------------------------------------------------------------------------------

0 commit comments

Comments
 (0)