[Haskell-cafe] a sort of chunk

Viktor Dukhovni ietf-dane at dukhovni.org
Sat Jan 18 00:08:37 UTC 2020


On Fri, Jan 17, 2020 at 05:41:12PM -0500, Viktor Dukhovni wrote:

> To your specific question I would refactor this a bit:
> 
>     -- | Split a list after the first element that reaches a target cumulative weight
>     --
>     splitWeight :: Int -> (a -> Int) -> [a] -> ([a], [a])
>     splitWeight target weight xs =
>         (,) <$> reverse . fst <*> snd $ go 0 xs []
>       where 
>         go _ [] acc = (acc, [])
>         go n (h:ts) acc
>             | let w = n + weight h
>             , w < target    = go w ts $ h : acc
>             | otherwise     = (h : acc, ts)

An alternative implementation of splitWeight is lazier, and produces the
first element of the initial chunk in constant time even when the target
weight is large and requires billions of elements (on a 64bit system):

    -- import Data.Bifunctor

    splitWeight' :: Int -> (a -> Int) -> [a] -> ([a], [a])
    splitWeight' target weight xs = go 0 xs
      where
        go _ [] = ([], [])
        go n (h:ts)
            | let w = n + weight h
            , w < target    = first (h:) $ go w ts
            | otherwise     = ([h], ts)

        -- Define directly, or import from Data.Bifunctor
        first f ~(a, b) = (f a, b)

λ> take 5  $ fst $ splitWeight' (maxBound :: Int) id [1..]
[1,2,3,4,5]

-- 
    Viktor.


More information about the Haskell-Cafe mailing list