on master help sign in
Add compareLength and some rewrite rules

--HG-- extra : convert_revision : 380b73e19fc0c193ba495103cbd1fdc657e0b7cc

commita02670d52eca033cceb2c7cab6d106f60f5aad7f authorBryan O'Sullivan <bos@serpentine.com> dateSat 21 Aug 2010, 06:34 (16 years ago) parent7264105
5 files changed+102−5
Data/Text.hs+53−1 Data/Text/Fusion/Common.hs+22−2 Data/Text/Fusion/Size.hs+6 Data/Text/Lazy.hs+16−1 tests/Properties.hs+5−1
Data/Text.hs+53−1
@@ -47,6 +47,7 @@ module Data.Text
4747 , init
4848 , null
4949 , length
50+ , compareLength
5051
5152 -- * Transformations
5253 , map
@@ -167,7 +168,7 @@ module Data.Text
167168 ) where
168169
169170import Prelude (Char, Bool(..), Functor(..), Int, Maybe(..), String,
170− Eq(..), Ord(..), (++),
171+ Eq(..), Ord(..), Ordering(..), (++),
171172 Read(..), Show(..),
172173 (&&), (||), (+), (-), (.), ($), ($!), (>>), (*),
173174 div, error, not, return, otherwise)
@@ -414,6 +415,51 @@ length :: Text -> Int
414415length t = S.length (stream t)
415416{-# INLINE length #-}
416417
418+-- | /O(n)/ Compare the count of characters in a 'Text' to a number.
419+-- Subject to fusion.
420+--
421+-- This function gives the same answer as comparing against the result
422+-- of 'length', but can short circuit if the count of characters is
423+-- greater than the number, and hence be more efficient.
424+compareLength :: Text -> Int -> Ordering
425+compareLength t n = S.compareLengthI (stream t) n
426+{-# INLINE [1] compareLength #-}
427+
428+{-# RULES
429+"TEXT compareN/length -> compareLength" [~1] forall t n.
430+ compare (length t) n = compareLength t n
431+ #-}
432+
433+{-# RULES
434+"TEXT ==N/length -> compareLength/==EQ" [~1] forall t n.
435+ (==) (length t) n = compareLength t n == EQ
436+ #-}
437+
438+{-# RULES
439+"TEXT /=N/length -> compareLength//=EQ" [~1] forall t n.
440+ (/=) (length t) n = compareLength t n /= EQ
441+ #-}
442+
443+{-# RULES
444+"TEXT <N/length -> compareLength/==LT" [~1] forall t n.
445+ (<) (length t) n = compareLength t n == LT
446+ #-}
447+
448+{-# RULES
449+"TEXT <=N/length -> compareLength//=GT" [~1] forall t n.
450+ (<=) (length t) n = compareLength t n /= GT
451+ #-}
452+
453+{-# RULES
454+"TEXT >N/length -> compareLength/==GT" [~1] forall t n.
455+ (>) (length t) n = compareLength t n == GT
456+ #-}
457+
458+{-# RULES
459+"TEXT >=N/length -> compareLength//=LT" [~1] forall t n.
460+ (>=) (length t) n = compareLength t n /= LT
461+ #-}
462+
417463-- -----------------------------------------------------------------------------
418464-- * Transformations
419465-- | /O(n)/ 'map' @f@ @t@ is the 'Text' obtained by applying @f@ to
@@ -461,6 +507,12 @@ replace s d = intercalate d . split s
461507-- case conversion rules. As a result, these functions may map one
462508-- input character to two or three output characters. For examples,
463509-- see the documentation of each function.
510+--
511+-- /Note/: In some languages, case conversion is a locale- and
512+-- context-dependent operation. The case conversion functions in this
513+-- module are /not/ locale sensitive. Programs that require locale
514+-- sensitivity should use appropriate versions of the case mapping
515+-- functions from the @text-icu@ package.
464516
465517-- | /O(n)/ Convert a string to folded case. This function is mainly
466518-- useful for performing caseless (also known as case insensitive)
Data/Text/Fusion/Common.hs+22−2
@@ -29,6 +29,7 @@ module Data.Text.Fusion.Common
2929 , init
3030 , null
3131 , lengthI
32+ , compareLengthI
3233 , isSingleton
3334
3435 -- * Transformations
@@ -99,8 +100,8 @@ module Data.Text.Fusion.Common
99100 ) where
100101
101102import Prelude (Bool(..), Char, Eq(..), Int, Integral, Maybe(..),
102− Ord(..), String, (.), ($), (+), (-), (*), (++), (&&),
103− fromIntegral, otherwise)
103+ Ord(..), Ordering(..), String, (.), ($), (+), (-), (*), (++),
104+ (&&), fromIntegral, otherwise)
104105import qualified Data.List as L
105106import qualified Prelude as P
106107import Data.Int (Int64)
@@ -266,6 +267,25 @@ lengthI (Stream next s0 _len) = loop_length 0 s0
266267 Yield _ s' -> loop_length (z + 1) s'
267268{-# INLINE[0] lengthI #-}
268269
270+-- | /O(n)/ Compares the count of characters in a string to a number.
271+-- Subject to fusion.
272+--
273+-- This function gives the same answer as comparing against the result
274+-- of 'lengthI', but can short circuit if the count of characters is
275+-- greater than the number, and hence be more efficient.
276+compareLengthI :: Integral a => Stream Char -> a -> Ordering
277+compareLengthI (Stream next s0 len) n =
278+ case exactly len of
279+ Nothing -> loop_cmp 0 s0
280+ Just i -> compare (fromIntegral i) n
281+ where
282+ loop_cmp !z s = case next s of
283+ Done -> compare z n
284+ Skip s' -> loop_cmp z s'
285+ Yield _ s' | z > n -> GT
286+ | otherwise -> loop_cmp (z + 1) s'
287+{-# INLINE[0] compareLengthI #-}
288+
269289-- | /O(n)/ Indicate whether a string contains exactly one element.
270290isSingleton :: Stream Char -> Bool
271291isSingleton (Stream next s0 _len) = loop 0 s0
Data/Text/Fusion/Size.hs+6
@@ -16,6 +16,7 @@
1616module Data.Text.Fusion.Size
1717 (
1818 Size
19+ , exactly
1920 , exactSize
2021 , maxSize
2122 , unknownSize
@@ -36,6 +37,11 @@ data Size = Exact {-# UNPACK #-} !Int -- ^ Exact size.
3637 | Unknown -- ^ Unknown size.
3738 deriving (Eq, Show)
3839
40+exactly :: Size -> Maybe Int
41+exactly (Exact n) = Just n
42+exactly _ = Nothing
43+{-# INLINE exactly #-}
44+
3945exactSize :: Int -> Size
4046exactSize n =
4147#if defined(ASSERTS)
Data/Text/Lazy.hs+16−1
@@ -55,6 +55,7 @@ module Data.Text.Lazy
5555 , init
5656 , null
5757 , length
58+ , compareLength
5859
5960 -- * Transformations
6061 , map
@@ -173,7 +174,7 @@ module Data.Text.Lazy
173174 ) where
174175
175176import Prelude (Char, Bool(..), Maybe(..), String,
176− Eq(..), Ord(..), Read(..), Show(..),
177+ Eq(..), Ord(..), Ordering, Read(..), Show(..),
177178 (&&), (+), (-), (.), ($), (++),
178179 div, error, flip, fromIntegral, not, otherwise)
179180import qualified Prelude as P
@@ -422,6 +423,20 @@ length = foldlChunks go 0
422423 S.length (stream t) = length t
423424 #-}
424425
426+-- | /O(n)/ Compare the count of characters in a 'Text' to a number.
427+-- Subject to fusion.
428+--
429+-- This function gives the same answer as comparing against the result
430+-- of 'length', but can short circuit if the count of characters is
431+-- greater than the number, and hence be more efficient.
432+compareLength :: Text -> Int64 -> Ordering
433+compareLength t n = S.compareLengthI (stream t) n
434+{-# INLINE [1] compareLength #-}
435+
436+-- We don't apply those otherwise appealing length-to-compareLength
437+-- rewrite rules here, because they can change the strictness
438+-- properties of code.
439+
425440-- | /O(n)/ 'map' @f@ @t@ is the 'Text' obtained by applying @f@ to
426441-- each element of @t@. Subject to array fusion.
427442map :: (Char -> Char) -> Text -> Text
tests/Properties.hs+5−1
@@ -200,6 +200,8 @@ s_length = length `eqP` S.length
200200sf_length p = (length . L.filter p) `eqP` (S.length . S.filter p)
201201t_length = length `eqP` T.length
202202tl_length = L.genericLength `eqP` TL.length
203+t_compareLength t = (compare (T.length t)) `eq` T.compareLength t
204+tl_compareLength t= (compare (TL.length t)) `eq` TL.compareLength t
203205
204206s_map f = map f `eqP` (unpackS . S.map f)
205207s_map_s f = map f `eqP` (unpackS . S.unstream . S.map f)
@@ -676,7 +678,9 @@ tests = [
676678 testProperty "s_length" s_length,
677679 testProperty "sf_length" sf_length,
678680 testProperty "t_length" t_length,
679− testProperty "tl_length" tl_length
681+ testProperty "tl_length" tl_length,
682+ testProperty "t_compareLength" t_compareLength,
683+ testProperty "tl_compareLength" tl_compareLength
680684 ],
681685
682686 testGroup "transformations" [