Search code examples
pythonrecursionpython-itertoolsrecurrencefunctools

Does Python have an iterative recursion generator function for first-order recurrence relations?


Is there a built in function or standard library function roughly equivalent to

def recur_until(start, step_fu, stop_predicate=lambda _: False):
    current = start
    while not stop_predicate(current):
        yield current
        current = step_fu(current)

or

def recur_while(start, step_fu, predicate=lambda _: True):
    current = start
    while predicate(current):
        yield current
        current = step_fu(current)

or even just

def recur(start, step_fu):
    current = start
    while True:
        yield current
        current = step_fu(current)

in any version of Python? (The latter is as good as the other two when combined with itertools.takewhile.)

A generator function like these would allow to compute certain recursively defined sequences iteratively, namely first-order recurrence relations.

While these aren't too hard to implement when needed, I feel like something like them should be part of itertools or maybe functools, but if it is, I haven't been able to spot it in the documentation, yet.


Usage examples:

list(recur_until(2, lambda x: x**2 - 1, lambda x: x > 1e4))
# [2, 3, 8, 63, 3968]

Should also work with non-number elements:

list(recur_until('', lambda x: '[{}]({})'.format(x, len(x)), lambda x: len(x) > 30))
# ['',
#  '[](0)',
#  '[[](0)](5)',
#  '[[[](0)](5)](10)',
#  '[[[[](0)](5)](10)](16)',
#  '[[[[[](0)](5)](10)](16)](22)']

Solution

  • The missing link is that you need something to convert your recursive stepping function into a generator. Once you have that, then you can use any of the itertools methods.

    def recur_to_gen(step_fu, current, sentinel=None):
        while current != sentinel:
            yield current
            current = step_fu(current)
    
    
    matches = itertools.takewhile(predicate, recur_to_gen(step_fu, start))
    

    recur_to_gen is probably a reasonable thing to propose adding to itertools.