I was reading this article and the following program that I have found there is confusing me:
def is_palindrome(num):
if num // 10 == 0:
return False
temp = num
reversed_num = 0
while temp != 0:
reversed_num = (reversed_num * 10) + (temp % 10)
temp = temp // 10
if num == reversed_num:
return True
else:
return False
def infinite_palindromes():
num = 0
while True:
if is_palindrome(num):
print("found a palindrome: "+str(num))
j = (yield num)
print("j = "+str(j))
if j is not None:
num = j
print("num = "+str(num))
num +=1
pal_gen = infinite_palindromes()
for i in pal_gen:
print("i = "+str(i))
digits = len(str(i))
pal_gen.send(10 ** (digits))
The output starts with:
I do not understand why after found a palindrome: 101, there do not appear i = 101 and j = 1000?
My thoughts: The program starts. With for i in pal_gen:, the program starts to run the while loop in infinite_palindromes(). When then num=10 and num += 1, the if clause is_palindrome is fullfilled. Then, when the program reads "yield", it continuous with print("i = "+str(i)). Then, with pal_gen.send(10 ** (digits)) it goes back to the generator at the line j = (yield num) and assigns to j the value 100. Then it assigns to num also 100 and then increases num to 101. Because this is a palindrome, we are again in the if clause, so the program prints "found a palindrome: 101". The next line that the program reads and executes is now again j = (yield num). And I thought that it is the same situation as before, so I imagined that after reading "yield", the program continuous at print("i = "+str(i))...and so on.
The problem is that you are mixing regular iteration and send
, this does not work: send
does not just inject a value into the generator it also runs an entire iteration and returns the next value.
So if you're using send
, you need to perform the entire iteration by hand, using either only send
(except for the first iteration which is necessarily a next
) or next
and send
.
Here because you're mixing send and regular iteration, you're skipping over every other iteration: an item gets extracted from the generator, then send
is called, injects a signal, and the next value of the generator is skipped / dropped.
This script shows the issue, the second snippet is what you are doing here:
def iterable():
v = 0
while v < 42:
n = yield v
v = (v if n is None else n) + 1
print("regular iteration")
for i in iterable():
print(i, end=" ")
print()
print("mix iteration and send")
it = iterable()
for i in it:
print(i, end=" ")
# you might thing this has no effect since it sends
# in the same value it got out, but it skips every
# other value
try:
it.send(i)
except StopIteration:
break
print()
print("only send")
it = iterable()
i = next(it)
while True:
print(i, end=" ")
# this is the correct way to handle it
try:
i = it.send(i)
except StopIteration:
break
print()
print("conditional send, mix with next")
it = iterable()
i = next(it)
while True:
print(i, end=" ")
# if you mix next and send it's fine as long as
# you're coherent and store every value
try:
if i % 2:
i = next(it)
else:
i = it.send(i)
except StopIteration:
break
print()
Since you're send-ing on every iteration, snippet 3 ("only send") is what you need to do.