RaisedByGods
RaisedByGods

Reputation: 1

Haskell: Highest sequence in a list that satisfies a predicate

I want a function takeUntill with the signature takeUntill :: (Int -> Bool) -> [Int] -> [Int].I did came out with the implementation but I'm looking for a cleaner solution to this problem than my existing one if it exists. My current solution is :

takeUntill :: (Int -> Bool) -> [Int] -> [Int]
takeUntill p a = snd $ maximum $ [(length x, x) | x <- groups p a]
groups :: (Int -> Bool) -> [Int] -> [[Int]]
groups _ [] = []
groups p a = ((takeWhile p a):(groups p (drop 1 $ dropWhile p a)))

Upvotes: 0

Views: 223

Answers (1)

Robin Zigmond
Robin Zigmond

Reputation: 18249

I think the way you are doing this looks OK, but the groups function is messy - particularly the way you drop exactly 1 from the elements that don't satisfy p at each step, which can result in your result having a lot of superfluous empty lists inside it. (These of course won't affect takeUntil provided at least 1 element of a satisfies p, but they're still there.)

I would rewrite groups as follows, using some useful library functions:

groups p a = filter (p . head) $ groupBy ((==) `on` p) a

I don't know if it's more efficient or not, but I certainly find it easier to read. To give some explanation, groupBy (http://hackage.haskell.org/package/base-4.12.0.0/docs/Data-List.html#v:groupBy) is from Data.List, and splits a list into a list of sublists according to whether a function of 2 consecutive arguments is true or not. And on (http://hackage.haskell.org/package/base-4.12.0.0/docs/Data-Function.html#v:on) is a handy little function from Data.Function which feeds the result of another function to the inputs of a binary function. The result is that

groupBy ((==) `on` p) a

splits the list up into sections where p is always True or always False. Then we filter that into just the parts that are True.

Upvotes: 2

Related Questions