Skip to content

Commit

Permalink
Add fold functions for arrays
Browse files Browse the repository at this point in the history
At the moment it is quite difficult to perform some folds on some
arrays.

* In general, arrays can be folded over by folding over the range of
  indices and then indexing into the array. This is a little cumbersome,
  and also inefficient when folding right-to-left because Ix does not
  offer reversed range generation.
* Alternately, Array has a Foldable instance which works great, but
  UArray cannot be Foldable. Folds on UArray can instead be done via
  elems. Due to list fusion, this works out well for some folds (foldr,
  foldl'), but not others (foldr').
* For mutable arrays, there are no alternate ways to fold.

This commit adds some commonly used folds for arrays and mutable arrays
to improve this situation.
  • Loading branch information
meooow25 authored and July541 committed Aug 24, 2024
1 parent 47143aa commit 9dd4cc3
Show file tree
Hide file tree
Showing 5 changed files with 187 additions and 0 deletions.
152 changes: 152 additions & 0 deletions Data/Array/Base.hs
Original file line number Diff line number Diff line change
Expand Up @@ -387,6 +387,94 @@ ixmap :: (IArray a e, Ix i, Ix j) => (i,i) -> (i -> j) -> a j e -> a i e
ixmap (l,u) f arr =
array (l,u) [(i, arr ! f i) | i <- range (l,u)]

-- | Lazy right-associative fold.
--
-- @since FIXME
foldrArray :: (IArray a e, Ix i) => (e -> b -> b) -> b -> a i e -> b
foldrArray f z = \a ->
let !n = numElements a
go i | i >= n = z
| otherwise = f (unsafeAt a i) (go (i+1))
in go 0
{-# INLINE foldrArray #-}

-- | Strict accumulating left-associative fold.
--
-- @since FIXME
foldlArray' :: (IArray a e, Ix i) => (b -> e -> b) -> b -> a i e -> b
foldlArray' f z0 = \a ->
let !n = numElements a
go !z i | i >= n = z
| otherwise = go (f z (unsafeAt a i)) (i+1)
in go z0 0
{-# INLINE foldlArray' #-}

-- | Lazy left-associative fold.
--
-- @since FIXME
foldlArray :: (IArray a e, Ix i) => (b -> e -> b) -> b -> a i e -> b
foldlArray f z = \a ->
let !n = numElements a
go i | i < 0 = z
| otherwise = f (go (i-1)) (unsafeAt a i)
in go (n-1)
{-# INLINE foldlArray #-}

-- | Strict accumulating right-associative fold.
--
-- @since FIXME
foldrArray' :: (IArray a e, Ix i) => (e -> b -> b) -> b -> a i e -> b
foldrArray' f z0 = \a ->
let !n = numElements a
go i !z | i < 0 = z
| otherwise = go (i-1) (f (unsafeAt a i) z)
in go (n-1) z0
{-# INLINE foldrArray' #-}

-- | Map elements to applicative actions, sequence them left-to-right, and
-- discard the results.
--
-- @since FIXME
traverseArray_
:: (IArray a e, Ix i, Applicative f) => (e -> f b) -> a i e -> f ()
traverseArray_ f = foldrArray (\x z -> f x *> z) (pure ())
{-# INLINE traverseArray_ #-}

-- | @forArray_@ is 'traverseArray_' with its arguments flipped.
--
-- @since FIXME
forArray_ :: (IArray a e, Ix i, Applicative f) => a i e -> (e -> f b) -> f ()
forArray_ = flip traverseArray_
{-# INLINE forArray_ #-}

-- | Strict accumulating left-associative monadic fold.
--
-- @since FIXME
foldlArrayM'
:: (IArray a e, Ix i, Monad m) => (b -> e -> m b) -> b -> a i e -> m b
foldlArrayM' f z0 = \a ->
let !n = numElements a
go !z i | i >= n = pure z
| otherwise = do
z' <- f z (unsafeAt a i)
go z' (i+1)
in go z0 0
{-# INLINE foldlArrayM' #-}

-- | Strict accumulating right-associative monadic fold.
--
-- @since FIXME
foldrArrayM'
:: (IArray a e, Ix i, Monad m) => (e -> b -> m b) -> b -> a i e -> m b
foldrArrayM' f z0 = \a ->
let !n = numElements a
go i !z | i < 0 = pure z
| otherwise = do
z' <- f (unsafeAt a i) z
go (i-1) z'
in go (n-1) z0
{-# INLINE foldrArrayM' #-}

-----------------------------------------------------------------------------
-- Normal polymorphic arrays

Expand Down Expand Up @@ -1025,6 +1113,70 @@ mapIndices (l',u') f marr = do
| i' <- range (l',u')]
return marr'

-- | Strict accumulating left-associative fold.
--
-- @since FIXME
foldlMArray' :: (MArray a e m, Ix i) => (b -> e -> b) -> b -> a i e -> m b
foldlMArray' f = foldlMArrayM' (\z x -> pure (f z x))
{-# INLINE foldlMArray' #-}

-- | Strict accumulating right-associative fold.
--
-- @since FIXME
foldrMArray' :: (MArray a e m, Ix i) => (e -> b -> b) -> b -> a i e -> m b
foldrMArray' f = foldrMArrayM' (\x z -> pure (f x z))
{-# INLINE foldrMArray' #-}

-- | Strict accumulating left-associative monadic fold.
--
-- @since FIXME
foldlMArrayM' :: (MArray a e m, Ix i) => (b -> e -> m b) -> b -> a i e -> m b
foldlMArrayM' f z0 = \a -> do
!n <- getNumElements a
let go !z i | i >= n = pure z
| otherwise = do
x <- unsafeRead a i
z' <- f z x
go z' (i+1)
go z0 0
{-# INLINE foldlMArrayM' #-}

-- | Strict accumulating right-associative monadic fold.
--
-- @since FIXME
foldrMArrayM' :: (MArray a e m, Ix i) => (e -> b -> m b) -> b -> a i e -> m b
foldrMArrayM' f z0 = \a -> do
!n <- getNumElements a
let go i !z | i < 0 = pure z
| otherwise = do
x <- unsafeRead a i
z' <- f x z
go (i-1) z'
go (n-1) z0
{-# INLINE foldrMArrayM' #-}

-- | Map elements to monadic actions, sequence them left-to-right, and discard
-- the results.
--
-- @since FIXME
mapMArrayM_ :: (MArray a e m, Ix i) => (e -> m b) -> a i e -> m ()
mapMArrayM_ f = \a -> do
!n <- getNumElements a
let go i | i >= n = pure ()
| otherwise = do
x <- unsafeRead a i
_ <- f x
go (i+1)
go 0
{-# INLINE mapMArrayM_ #-}

-- | @forMArrayM_@ is 'mapMArrayM_' with its arguments flipped.
--
-- @since FIXME
forMArrayM_ :: (MArray a e m, Ix i) => a i e -> (e -> m b) -> m ()
forMArrayM_ = flip mapMArrayM_
{-# INLINE forMArrayM_ #-}

-----------------------------------------------------------------------------
-- Polymorphic non-strict mutable arrays (ST monad)

Expand Down
10 changes: 10 additions & 0 deletions Data/Array/IArray.hs
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,16 @@ module Data.Array.IArray (
elems, -- :: (IArray a e, Ix i) => a i e -> [e]
assocs, -- :: (IArray a e, Ix i) => a i e -> [(i, e)]

-- * Array folds
foldrArray,
foldlArray',
foldlArray,
foldrArray',
traverseArray_,
forArray_,
foldlArrayM',
foldrArrayM',

-- * Incremental array updates
(//), -- :: (IArray a e, Ix i) => a i e -> [(i, e)] -> a i e
accum, -- :: (IArray a e, Ix i) => (e -> e' -> e) -> a i e -> [(i, e')] -> a i e
Expand Down
8 changes: 8 additions & 0 deletions Data/Array/MArray.hs
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,14 @@ module Data.Array.MArray (
modifyArray,
modifyArray',

-- * Array folds
foldlMArray',
foldrMArray',
mapMArrayM_,
forMArrayM_,
foldlMArrayM',
foldrMArrayM',

-- * Derived arrays
mapArray, -- :: (MArray a e' m, MArray a e m, Ix i) => (e' -> e) -> a i e' -> m (a i e)
mapIndices, -- :: (MArray a e m, Ix i, Ix j) => (i,i) -> (i -> j) -> a j e -> m (a i e)
Expand Down
8 changes: 8 additions & 0 deletions Data/Array/MArray/Safe.hs
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,14 @@ module Data.Array.MArray.Safe (
readArray, -- :: (MArray a e m, Ix i) => a i e -> i -> m e
writeArray, -- :: (MArray a e m, Ix i) => a i e -> i -> e -> m ()

-- * Array folds
foldlMArray',
foldrMArray',
mapMArrayM_,
forMArrayM_,
foldlMArrayM',
foldrMArrayM',

-- * Derived arrays
mapArray, -- :: (MArray a e' m, MArray a e m, Ix i) => (e' -> e) -> a i e' -> m (a i e)
mapIndices, -- :: (MArray a e m, Ix i, Ix j) => (i,i) -> (i -> j) -> a j e -> m (a i e)
Expand Down
9 changes: 9 additions & 0 deletions changelog.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,14 @@
# Changelog for [`array` package](http://hackage.haskell.org/package/array)

## Next release

### Added

* Folds for arrays: `foldrArray`, `foldlArray'`, `foldlArray`, `foldrArray'`,
`traverseArray_`, `forArray_`, `foldlArrayM'`, `foldrArrayM'`.
* Folds for mutable arrays: `foldlMArray'`, `foldrMArray'`, `mapMArrayM_`,
`forMArrayM_`, `foldlMArrayM'`, `foldrMArrayM'`.

## 0.5.6.0 *July 2023*

### Changed
Expand Down

0 comments on commit 9dd4cc3

Please sign in to comment.