Home > front end >  Why does python generator result like this?
Why does python generator result like this?

Time:12-31

Here's the code:

def is_subsequence(a, b):
    b = iter(b)
    
    gen = ((i in b) for i in a)
    print(gen)

    for i in gen:
        print(i)

    return all(((i in b) for i in a))

res1 = is_subsequence([1, 3, 5], [1, 2, 3, 4, 5])
res2 = is_subsequence([1, 4, 3], [1, 2, 3, 4, 5])

The result is that res1==False and res2==False. Apparently the result for res1 is incorrect. When I comment out the for loop print, res1==True and res2==False, which is correct. I'm confused. Can someone explain why?

CodePudding user response:

Generators are lazy iterators. You can loop over them like a list but once you do, they are exhausted, i.e. nothing is left. With the following line,

gen = ((i in b) for i in a)
print(gen)

you create a generator object and then with the following loop,

for i in gen:
    print(i)

you exhaust it. Incidentally you also exhaust b while exhausting gen. So by the time of the last line, b is empty, so your function will always return False.

IIUC, you want to evaluate if a is a subsequence (not subset) of b. So the order matters. You can change your function to:

def is_subsequence(a, b):
    b = iter(b)
    return all(((i in b) for i in a))

and it will work as intended.

Output:

res1 = is_subsequence([1, 3, 5], [1, 2, 3, 4, 5])  # True
res2 = is_subsequence([1, 4, 3], [1, 2, 3, 4, 5])  # False

CodePudding user response:

The main issue you have is with the expression i in b when b is an iterator. Checking membership in an iterator iterates over the values. It returns True if the value gets found (consuming values from the iterator up to and including the one found), or False if the iterator runs out first. Unfortunately, if you ask for values that the iterator will yield in the wrong order, you can easily miss some of them. Here's a simpler example of that:

b = iter([1,2,3])
print(1 in b) # prints True
print(3 in b) # prints True, but skipped over 2 to get there
print(2 in b) # prints False, since the iterator is exhausted after seeing 3
  • Related