目的:更熟悉應用generator。
參考:https://www.liaoxuefeng.com/wiki/1016959663602400/1017404530360000
素數定義:
素數:質數又稱素數。一個大於1的自然數,除了1和它自身外,不能被其他自然數整除的數叫做質數。
方法:
首先,列出從2
開始的所有自然數,構造一個序列:
2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, ...
第二步,取序列的第一個數2
,它一定是素數,然后用2
把序列的2
的倍數篩掉:
3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, ...
⚠️此時的序列是從3開始的奇數集合。
第三步,取新序列的第一個數3
,它一定是素數,然后用3
把序列的3
的倍數篩掉:
5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, ...
第四步,取新序列的第一個數5
,然后用5
把序列的5
的倍數篩掉:
7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, ...
后續,不斷篩下去,就可以得到所有的素數。
代碼:
2是素數,直接從一個奇數序列開始篩選:
# 生成一個奇數序列。從3開始 def _odd_iter(): n = 1 while True: n += 2 yield n
篩選函數:
- 由上面的分析可知,每次篩選,都除以序列的第一數。
- 條件是x % n > 0。x為序列中被判斷的數字,n為該序列的第一個數字。整除結果==0的都不是素樹。要從序列中刪除。
- 所以,判斷表達式是不固定的。定義一個函數,傳入參數n, 返回一個lambda表達式用於判斷。
# 傳入參數n,生成不同的lambda表達式。 def _not_divisible(n): # 返回一個lambda表達式。用於判斷x,是不是素數。 # 素數,只能被1和自身整除的自然數叫做素數。 # 整除就是余數為0。 # 如果被非自身數字整除,即余數為0,那自然不是素數,刪除掉。 return lambda x: x % n > 0
主函數:
- 第一行代碼:yield 2,返回素數2。
- 第二行代碼:創建生成器,第一次運行,它產生奇數序列,這個序列會逐步被篩選,去掉非素數字。
- 循環代碼:
- n = next(it), 得到序列的第一個數字。
- yield n, 它是素數。
- 用filter()來篩選, it生成器中的數字。 因為it是可迭代的。並把新的序列賦值給it變量。
⚠️重新理解3。filter函數對生成器對象it進行篩選,其實就是代碼組合,把_not_divisible(n)返回的lambda表達式和it自身的代碼組合,形成新的對象<filter object>。
⚠️我的理解是,原先的_odd_iter()產生的算法不再用,而是使用增加了lambda條件判斷的新的算法,<filter object>。
⚠️這一步並不是真的篩選掉不符合的數字,_ood_iter()只是代表了生成序列數字的算法。只不過這個算法被新的算法替代了。
def primes(): yield 2 #2是素數。 it = _odd_iter() #創建一個<generator object _odd_iter>,代表奇數序列。 while True: n = next(it) #返回奇數序列的一個數。 yield n #這個數是素數。所以返回。 # 創建一個<filter object>, 代表新序列。 it = filter(_not_divisible(n), it)
由於primes()所代表的算法生成的
是一個無限序列,沒有退出機制,所以調用時需要設置一個退出循環的條件:
for n in primes(): if n < 1000: print(n) else: break
⚠️primes本身是生成器函數, primes()就是一個生成器,它自身不儲存數據,只儲存生成數據的算法。
總結;
由本例可知,generator其實就是算法,它不會一次性產生全部數據,而是根據調用的次數,逐步向內存寫入數據。
本例把一個生成器對象作為參數傳入了filter()函數,其實就是對這個生成器的代碼進行更新,即改進算法。最后返回一個新的迭代器。本例返回的是<filter object>。