On Hackage I see that groupBy
's implementation is this:
groupBy :: (a -> a -> Bool) -> [a] -> [[a]]
groupBy _ [] = []
groupBy eq (x:xs) = (x:ys) : groupBy eq zs
where (ys,zs) = span (eq x) xs
which means that the predicate eq
holds between any two elements of each group. Examples:
> difference_eq_1 = ((==1).) . flip (-)
> first_isnt_newline = ((/= '\n').) . const
>
> Data.List.groupBy difference_eq_1 ([1..10] ++ [11,13..21])
[[1,2],[3,4],[5,6],[7,8],[9,10],[11],[13],[15],[17],[19],[21]]
>
> Data.List.groupBy first_isnt_newline "uno\ndue\ntre"
["uno\ndue\ntre"]
What if instead I want to group elements such that the predicate holds between any pair of consecutive elements, so that the above results would be as follows?
[[1,2,3,4,5,6,7,8,9,10,11],[13],[15],[17],[19],[21]]
["uno\n","due\n","tre"]
I wrote it myself, and it looks a bit ugly
groupBy' :: (a -> a -> Bool) -> [a] -> [[a]]
groupBy' p = foldr step []
where step elem [] = [[elem]]
step elem gs'@((g'@(prev:g)):gs)
| elem `p` prev = (elem:g'):gs
| otherwise = [elem]:gs'
So I was wandering if such a function exists already and I just don't find it.
As regards the second usage, Data.List.groupBy first_isnt_newline
, where the binary predicate basically ignores the second argument and applies a unary predicate to the first, I've just found that Data.List.HT.segmentAfter unary_predicate
does the job, where unary_predicate
is the negation of the unary predicate in which const
's output is forwarded. In other words Data.List.groupBy ((/= '\n').) . const === Data.List.HT.segmentAfter (=='\n')
.