Search code examples
pythongenerator

Generator function yield the largest iterable value


I'm a beginner for python, and I'm currently preparing a test for my class. There is one part I'm confused about on one question.

We are asked to create a generator function that only yields the result that is from the largest iterable arguments after all other iterable arguments stop their iteration.

So, my function currently looks like this:

def generator(*args):
    args = [iter(a) for a in args]
    while True:
        yielded = False
        for a in args:
            try:
                yield next(a)
                yielded = True
            except StopIteration:
                pass
        if not yielded:
            return
a = generator('abc', 'abcdef', [1,2])
print([i for i in a])

>>> ['a', 'a', 1, 'b', 'b', 2, 'c', 'c', 'd', 'e', 'f']

But what I want is since the argument 'abcdef', it will not stop iteration when all other parameters stop iteration right? I want to only print ['d','e','f']

So, the desired structure for this should be:

def generator(*args):
    blah blah

a = generator('abc', 'abcdef', [1,2])
print([i for i in a])

>>>['d','e','f']

Is there anything wrong with my function?


Solution

  • The task will be easier if you define some helper functions

    vals = ['abc', 'abcdef', [1, 2]]
    
    
    def next_or_none(iterator):
        """Return the next item in the iterator, or None if the iterator is done.
        """
        try:
            return next(iterator)
        except StopIteration:
            return None
    
    
    def myzip(*args):
        """Yield one element from each arg, yield None for arg that are done.
        """
        args = [iter(a) for a in args]
        while True:
            res = [next_or_none(a) for a in args]
            if all(v is None for v in res):  # make sure we stop when all iterators are exhausted
                return
            yield res
    

    with those defined all you need to do is loop through the myzip generator until it provides only one value per iteration.

    def remainder(*args):
        for nextarg in myzip(*args):
            notnones = [v for v in nextarg if v is not None]
            if len(notnones) == 1:
                # wait for all but one iterator to be done before yielding.
                yield notnones[0]
    
    print(list(remainder(*vals)))