ilist 0.3.0.0 → 0.3.1.0
raw patch · 6 files changed
+77/−5 lines, 6 filesdep +loopPVP ok
version bump matches the API change (PVP)
Dependencies added: loop
API changes (from Hackage documentation)
+ Data.List.Index: ireplicateM :: Applicative m => Int -> (Int -> m a) -> m [a]
+ Data.List.Index: ireplicateM_ :: Monad m => Int -> (Int -> m a) -> m ()
Files
- CHANGELOG.md +4/−0
- bench/Functions.hs +17/−1
- bench/Main.hs +10/−0
- ilist.cabal +3/−2
- lib/Data/List/Index.hs +26/−0
- tests/Main.hs +17/−2
CHANGELOG.md view
@@ -1,3 +1,7 @@+# 0.3.1.0++* Added `ireplicateM` and `ireplicateM_`.+ # 0.3.0.0 * `ifind` now returns the index alongside with the value (same as in `lens`).
bench/Functions.hs view
@@ -1,6 +1,7 @@ {-# LANGUAGE MagicHash,-BangPatterns+BangPatterns,+CPP #-} @@ -16,6 +17,7 @@ import Data.List import Data.List.Index import Control.Monad+import qualified Control.Loop as Loop indexed_zip :: [a] -> [(Int, a)]@@ -93,6 +95,20 @@ f (I# i) x go (i +# 1#) xs {-# INLINE imapM__rec #-}++#if __GLASGOW_HASKELL__ < 710+ireplicateM__loop+ :: (Monad m, Functor m) => Int -> (Int -> m a) -> m ()+#else+ireplicateM__loop+ :: Monad m => Int -> (Int -> m a) -> m ()+#endif+ireplicateM__loop n f = Loop.numLoop 0 (n-1) (void . f)+{-# INLINE ireplicateM__loop #-}++ireplicateM__for :: Monad m => Int -> (Int -> m a) -> m ()+ireplicateM__for n f = forM_ [0..n-1] f+{-# INLINE ireplicateM__for #-} iall_zip :: (Int -> a -> Bool) -> [a] -> Bool iall_zip p xs = and (zipWith p [0..] xs)
bench/Main.hs view
@@ -106,6 +106,16 @@ bench "lens" $ nf (\n -> flip runState [] $ L.imapM_ (\i x -> modify ((i+x):) >> return (i-x)) [0..n]) 100000, bench "our" $ nf (\n -> flip runState [] $ imapM_ (\i x -> modify ((i+x):) >> return (i-x)) [0..n]) 100000 ] ], + bgroup "ireplicateM_" [+ bgroup "Just" [+ bench "loop" $ nf (\n -> ireplicateM__loop n (\i -> if i<50000 then Just i else Nothing)) 100000,+ bench "for" $ nf (\n -> ireplicateM__for n (\i -> if i<50000 then Just i else Nothing)) 100000,+ bench "our" $ nf (\n -> ireplicateM_ n (\i -> if i<50000 then Just i else Nothing)) 100000 ],+ bgroup "State" [+ bench "loop" $ nf (\n -> flip runState 0 $ ireplicateM__loop n (\i -> modify' (i+))) 100000,+ bench "for" $ nf (\n -> flip runState 0 $ ireplicateM__for n (\i -> modify' (i+))) 100000,+ bench "our" $ nf (\n -> flip runState 0 $ ireplicateM_ n (\i -> modify' (i+))) 100000 ] ],+ bgroup "ifilter" [ bgroup "consume" [ bench "rec" $ nf (\n -> sum $ ifilter_rec (\i x -> rem (i+x) 5000 == 0) [0..n]) 100000,
ilist.cabal view
@@ -1,8 +1,8 @@ name: ilist-version: 0.3.0.0+version: 0.3.1.0 synopsis: Optimised list functions for doing index-related things description:- Optimised list functions for doing index-related things. They're faster than common idioms in all cases, and sometimes they fuse better as well.+ Optimised list functions for doing index-related things. They're faster than common idioms in all cases, they avoid <https://ghc.haskell.org/trac/ghc/ticket/12620 space leaks>, and sometimes they fuse better as well. homepage: http://github.com/aelve/ilist bug-reports: http://github.com/aelve/ilist/issues license: BSD3@@ -49,6 +49,7 @@ , ilist -- imapM_ is broken in 4.13.2 , lens >= 4.13.2.1+ , loop , transformers , vector ghc-options: -O2 -Wall -fno-warn-unused-do-bind
lib/Data/List/Index.hs view
@@ -58,6 +58,7 @@ -- ** Monadic functions iforM, iforM_, itraverse, itraverse_,+ ireplicateM, ireplicateM_, ifoldrM, ifoldlM, @@ -341,6 +342,31 @@ ifor_ :: Applicative m => [a] -> (Int -> a -> m b) -> m () ifor_ = flip itraverse_ {-# INLINE ifor_ #-}++{- |+Perform a given action @n@ times. Behaves like @for_ [0..n-1]@, but avoids <https://ghc.haskell.org/trac/ghc/ticket/12620 space leaks>.++If you want more complicated loops (e.g. counting downwards), consider the <https://hackage.haskell.org/package/loop loop> package.+-}+ireplicateM :: Applicative m => Int -> (Int -> m a) -> m [a]+ireplicateM cnt f = go 0+ where+ go !i | i >= cnt = pure []+ | otherwise = (:) <$> f i <*> go (i + 1)+{-# INLINE ireplicateM #-}++{- |+NB. This function intentionally uses 'Monad' even though 'Applicative' is enough. That's because the @transformers@ package didn't have an optimized definition of ('*>') for 'StateT' prior to 0.5.3.0, so for a common case of 'StateT' this function would be 40 times slower with the 'Applicative' constraint.+-}+ireplicateM_ :: Monad m => Int -> (Int -> m a) -> m ()+ireplicateM_ cnt f = if cnt > 0 then go 0 else return ()+ where+ -- this is 30% faster for Maybe than the simpler+ -- go i | i == cnt = return ()+ -- | otherwise = f i >> go (i + 1)+ cnt_ = cnt-1+ go !i = if i == cnt_ then f i >> return () else f i >> go (i + 1)+{-# INLINE ireplicateM_ #-} -- Using unboxed ints here doesn't seem to result in any benefit ifoldr :: (Int -> a -> b -> b) -> b -> [a] -> b
tests/Main.hs view
@@ -136,7 +136,7 @@ specify "basic" $ do let f i x = modify ((i,x):) >> return (i-x) let (resA, stA) = runState (imapM f [1,3..9]) []- let (resB, stB) = runState (itraverse f [1,3..9]) []+ (resB, stB) = runState (itraverse f [1,3..9]) [] resA `shouldBe` [0-1,1-3,2-5,3-7,4-9] resB `shouldBe` [0-1,1-3,2-5,3-7,4-9] stA `shouldBe` reverse (zip [0..4] [1,3..9])@@ -154,9 +154,24 @@ specify "basic" $ do let f i x = modify ((i,x):) >> return (i-x) let stA = execState (imapM_ f [1,3..9]) []- let stB = execState (itraverse_ f [1,3..9]) []+ stB = execState (itraverse_ f [1,3..9]) [] stA `shouldBe` reverse (zip [0..4] [1,3..9]) stB `shouldBe` reverse (zip [0..4] [1,3..9])++ describe "ireplicateM" $ do+ describe "State" $ do+ specify "basic" $ do+ let f i = modify (i:) >> return ((i+1)*2)+ let (res, st) = runState (ireplicateM 5 f) []+ res `shouldBe` [2,4..10]+ st `shouldBe` reverse [0..4]++ describe "ireplicateM_" $ do+ describe "State" $ do+ specify "basic" $ do+ let f i = modify (i:) >> return ((i+1)*2)+ let st = execState (ireplicateM_ 5 f) []+ st `shouldBe` reverse [0..4] specialFolds :: Spec specialFolds = describe "special folds" $ do