{-# LANGUAGE CPP #-}{-# LANGUAGE BangPatterns #-} #ifdef __GLASGOW_HASKELL__ {-# LANGUAGE Trustworthy #-} #endif ------------------------------------------------------------------------------- |-- Module : Data.Containers.ListUtils-- Copyright : (c) Gershom Bazerman 2018-- License : BSD-style-- Maintainer : libraries@haskell.org-- Portability : portable---- This module provides efficient containers-based functions on the list type.---- In the documentation, \(n\) is the number of elements in the list while-- \(d\) is the number of distinct elements in the list. \(W\) is the number-- of bits in an 'Int'.---- @since 0.6.0.1-----------------------------------------------------------------------------moduleData.Containers.ListUtils(nubOrd ,nubOrdOn ,nubInt ,nubIntOn )whereimportData.Set (Set )importqualifiedData.Set asSetimportqualifiedData.IntSet asIntSetimportData.IntSet (IntSet ) #ifdef __GLASGOW_HASKELL__ importGHC.Exts(build) #endif -- *** Ord-based nubbing ***-- | \( O(n \log d) \). The @nubOrd@ function removes duplicate elements from a-- list. In particular, it keeps only the first occurrence of each element. By-- using a 'Set' internally it has better asymptotics than the standard-- 'Data.List.nub' function.---- ==== Strictness---- @nubOrd@ is strict in the elements of the list.---- ==== Efficiency note---- When applicable, it is almost always better to use 'nubInt' or 'nubIntOn'-- instead of this function, although it can be a little worse in certain-- pathological cases. For example, to nub a list of characters, use---- @ nubIntOn fromEnum xs @---- @since 0.6.0.1nubOrd ::Orda =>[a ]->[a ]nubOrd :: forall a. Ord a => [a] -> [a] nubOrd =(a -> a) -> [a] -> [a] forall b a. Ord b => (a -> b) -> [a] -> [a] nubOrdOn a -> a forall a. a -> a id{-# INLINEnubOrd #-}-- | The @nubOrdOn@ function behaves just like 'nubOrd' except it performs-- comparisons not on the original datatype, but a user-specified projection-- from that datatype.---- ==== Strictness---- @nubOrdOn@ is strict in the values of the function applied to the-- elements of the list.---- @since 0.6.0.1nubOrdOn ::Ordb =>(a ->b )->[a ]->[a ]-- For some reason we need to write an explicit lambda here to allow this-- to inline when only applied to a function.nubOrdOn :: forall b a. Ord b => (a -> b) -> [a] -> [a] nubOrdOn a -> b f =\[a] xs ->(a -> b) -> Set b -> [a] -> [a] forall b a. Ord b => (a -> b) -> Set b -> [a] -> [a] nubOrdOnExcluding a -> b f Set b forall a. Set a Set.empty [a] xs {-# INLINEnubOrdOn #-}-- Splitting nubOrdOn like this means that we don't have to worry about-- matching specifically on Set.empty in the rewrite-back rule.nubOrdOnExcluding ::Ordb =>(a ->b )->Set b ->[a ]->[a ]nubOrdOnExcluding :: forall b a. Ord b => (a -> b) -> Set b -> [a] -> [a] nubOrdOnExcluding a -> b f =Set b -> [a] -> [a] go wherego :: Set b -> [a] -> [a] go Set b _[]=[]go Set b s (a x :[a] xs )|b fx b -> Set b -> Bool forall a. Ord a => a -> Set a -> Bool `Set.member` Set b s =Set b -> [a] -> [a] go Set b s [a] xs |Bool otherwise=a x a -> [a] -> [a] forall a. a -> [a] -> [a] :Set b -> [a] -> [a] go (b -> Set b -> Set b forall a. Ord a => a -> Set a -> Set a Set.insert b fx Set b s )[a] xs where!fx :: b fx =a -> b f a x #ifdef __GLASGOW_HASKELL__ -- We want this inlinable to specialize to the necessary Ord instance.{-# INLINABLE[1]nubOrdOnExcluding #-}{-# RULES-- Rewrite to a fusible form."nubOrdOn"[~1]forallf as s .nubOrdOnExcluding f s as =build(\c n ->foldr(nubOrdOnFB f c )(constNubOn n )as s )-- Rewrite back to a plain form"nubOrdOnList"[1]forallf as s .foldr(nubOrdOnFB f (:))(constNubOn [])as s =nubOrdOnExcluding f s as #-}nubOrdOnFB ::Ordb =>(a ->b )->(a ->r ->r )->a ->(Set b ->r )->Set b ->r nubOrdOnFB :: forall b a r. Ord b => (a -> b) -> (a -> r -> r) -> a -> (Set b -> r) -> Set b -> r nubOrdOnFB a -> b f a -> r -> r c a x Set b -> r r Set b s |b fx b -> Set b -> Bool forall a. Ord a => a -> Set a -> Bool `Set.member` Set b s =Set b -> r r Set b s |Bool otherwise=a x a -> r -> r `c` Set b -> r r (b -> Set b -> Set b forall a. Ord a => a -> Set a -> Set a Set.insert b fx Set b s )where!fx :: b fx =a -> b f a x {-# INLINABLE[0]nubOrdOnFB #-}constNubOn ::a ->b ->a constNubOn :: forall a b. a -> b -> a constNubOn a x b _=a x {-# INLINE[0]constNubOn #-} #endif -- *** Int-based nubbing ***-- | \( O(n \min(d,W)) \). The @nubInt@ function removes duplicate 'Int'-- values from a list. In particular, it keeps only the first occurrence-- of each element. By using an 'IntSet' internally, it attains better-- asymptotics than the standard 'Data.List.nub' function.---- See also 'nubIntOn', a more widely applicable generalization.---- ==== Strictness---- @nubInt@ is strict in the elements of the list.---- @since 0.6.0.1nubInt ::[Int]->[Int]nubInt :: [Int] -> [Int] nubInt =(Int -> Int) -> [Int] -> [Int] forall a. (a -> Int) -> [a] -> [a] nubIntOn Int -> Int forall a. a -> a id{-# INLINEnubInt #-}-- | The @nubIntOn@ function behaves just like 'nubInt' except it performs-- comparisons not on the original datatype, but a user-specified projection-- from that datatype. For example, @nubIntOn 'fromEnum'@ can be used to-- nub characters and typical fixed-with numerical types efficiently.---- ==== Strictness---- @nubIntOn@ is strict in the values of the function applied to the-- elements of the list.---- @since 0.6.0.1nubIntOn ::(a ->Int)->[a ]->[a ]-- For some reason we need to write an explicit lambda here to allow this-- to inline when only applied to a function.nubIntOn :: forall a. (a -> Int) -> [a] -> [a] nubIntOn a -> Int f =\[a] xs ->(a -> Int) -> IntSet -> [a] -> [a] forall a. (a -> Int) -> IntSet -> [a] -> [a] nubIntOnExcluding a -> Int f IntSet IntSet.empty [a] xs {-# INLINEnubIntOn #-}-- Splitting nubIntOn like this means that we don't have to worry about-- matching specifically on IntSet.empty in the rewrite-back rule.nubIntOnExcluding ::(a ->Int)->IntSet ->[a ]->[a ]nubIntOnExcluding :: forall a. (a -> Int) -> IntSet -> [a] -> [a] nubIntOnExcluding a -> Int f =IntSet -> [a] -> [a] go wherego :: IntSet -> [a] -> [a] go IntSet _[]=[]go IntSet s (a x :[a] xs )|Int fx Int -> IntSet -> Bool `IntSet.member` IntSet s =IntSet -> [a] -> [a] go IntSet s [a] xs |Bool otherwise=a x a -> [a] -> [a] forall a. a -> [a] -> [a] :IntSet -> [a] -> [a] go (Int -> IntSet -> IntSet IntSet.insert Int fx IntSet s )[a] xs where!fx :: Int fx =a -> Int f a x #ifdef __GLASGOW_HASKELL__ -- We don't mark this INLINABLE because it doesn't seem obviously useful-- to inline it anywhere; the elements the function operates on are actually-- pulled from a list and installed in a list; the situation is very different-- when fusion occurs. In this case, we let GHC make the call.{-# NOINLINE[1]nubIntOnExcluding #-}{-# RULES"nubIntOn"[~1]forallf as s .nubIntOnExcluding f s as =build(\c n ->foldr(nubIntOnFB f c )(constNubOn n )as s )"nubIntOnList"[1]forallf as s .foldr(nubIntOnFB f (:))(constNubOn [])as s =nubIntOnExcluding f s as #-}nubIntOnFB ::(a ->Int)->(a ->r ->r )->a ->(IntSet ->r )->IntSet ->r nubIntOnFB :: forall a r. (a -> Int) -> (a -> r -> r) -> a -> (IntSet -> r) -> IntSet -> r nubIntOnFB a -> Int f a -> r -> r c a x IntSet -> r r IntSet s |Int fx Int -> IntSet -> Bool `IntSet.member` IntSet s =IntSet -> r r IntSet s |Bool otherwise=a x a -> r -> r `c` IntSet -> r r (Int -> IntSet -> IntSet IntSet.insert Int fx IntSet s )where!fx :: Int fx =a -> Int f a x {-# INLINABLE[0]nubIntOnFB #-} #endif