Search code examples
haskellfunctional-programminggenerator

Equivalent in Haskell of generator with stopping condition


Suppose I want to build a list iterate step x0 in Haskell, but with an inclusive termination condition. So in Python this would be list(my_gen) where e.g.

def my_gen():
    x = x0
    while not done(x):
        x = step(x)
        yield x

(Edit: This should have an additional yield x before the loop if I want to include x0.)

One way would be to write my own takeWhileInclusive and say

takeWhileInclusive (not . done) . iterate step x0

Is this the Haskell-y way (or a Haskell-y way) to accomplish this? It seems unnatural to try to tack on some sentinel value for step x when done x is true and then use takeWhile.

In particular I'm thinking of the container with most water problem on LeetCode, and solving it with something like

maxWith volume . smartSteps (0, n)
 where smartSteps = takeWhileInclusive (\(i,j) -> j - i > 1) . iterate step

and step increases i or decreases j (or both), according to which index has the higher line.

Of course here it would be easy to just use takeWhile j > i, but I wanted to think how I would approach situations where there isn't a natural "you went too far" condition, just a "you are done" condition.

Edit: This question has been marked as a duplicate (of a question which I had linked to in my question), but it is not. The question is not how to write takeWhileInclusive, in fact the question explicitly takes takeWhileInclusive as given. It is about how to accomplish a task that may or may not use takeWhileInclusive.


Solution

  • You can define

    takeUntil done xs = 
      foldr (\x r -> if done x then [x] else x : r) [] xs
    

    and then use it like

    takeUntil done $ iterate step x0
    

    e.g. takeUntil (> 9) [1..] == [1..10].

    It's easy to specify the final element with foldr (as is seen here), but more cumbersome to do that with unfoldr which encodes an "anamorphism", closing the generated list with an empty list as the sentinel. Specifying the non-empty tail is possible with an "apomorphism", which seems like it would be the fitting tool for this task.