1. 程式人生 > >關於用埃氏篩選法求素數python程式碼的一些理解

關於用埃氏篩選法求素數python程式碼的一些理解

原始碼

來自廖雪峰-filter
演算法描述參考原文。

程式碼塊如下:

def _odd_iter(): # 生成一個無限序列的奇數Generator
    z = 1
    while True:
        z = z+2
        yield z 

def _not_divisible(y): # 用埃氏篩選法過濾掉奇數序列中的合數
    return lambda x : x % y > 0

def primes():
    yield 2
    it = _odd_iter() # 初始序列
    while True:
        n = next(it) # 返回序列的第一個數
        yield n
        it = filter(_not_divisible(n), it) # 構造新序列

for n in primes(): # 列印前1000個數中的素數
    if n < 1000:
        print(n)
    else:
        break

疑問

將語句it = filter(_not_divisible(n), it) 修改成it = filter(lambda x : x % n >0, it)這句,表面看起來似乎等價,執行結果卻完全不同。
語句為it = filter(lambda x : x % n >0, it)的執行結果
語句為it = filter(_not_divisible(n), it)的執行結果
很明顯,當語句為it = filter(lambda x : x % y >0, it),執行的結果是不對的。這涉及到閉包。“閉包本質上是一個函式,它有兩部分組成,一個函式和一個變數,閉包使得這些變數的值始終儲存在記憶體中。”聽不懂對不對?沒關係,我也理解的似是而非,以後使用場景多了理解更深了再來更新。具體到這個例子,使用it = filter(lambda x : x % n>0, it)這條語句,當執行到奇數9的時候,x=9,而n一直傳入的是7,自然達不到過濾的目的。
事實上,等價寫法應該是 it = _filter(lambda x,y=n: x % y >0, it)
那麼當使用語句it = filter(_not_divisible(n), it) 的時候,例如當執行到奇數7的時候,為什麼y的值會是3和5呢?
看了這個答案後(

lambda閉包-閉包到底捕獲了哪個引數?),我理解為這裡的y值為3的這個引數不是後來傳入進去的,而是上一次已經把_not_divisible(3)存成一個閉包函數了,以後同理,會存
_not_divisible(5),_not_divisible(7)……這些閉包函式。
在這裡插入圖片描述
在這裡插入圖片描述
在這裡插入圖片描述
在這裡插入圖片描述

為了驗證這個想法,我們把程式改下一下:

arrlist = [] # 宣告一個全域性list,用來儲存閉包函式

def _odd_iter():
    z = 1
    while True:
        z = z+2
        yield z

def _not_divisible(y):
    def _f(x):
        return  x % y > 0
    return _f
    #return lambda x : x % y > 0

def _filter(func,seq):
    # filter_list = []
    for s in seq:
        if func(s):
            if( not(func in arrlist)): 
                arrlist.append(func) # 追加程式執行時產生的新的閉包函式
            # filter_list.append(s)
            yield s

def primes():
    yield 2
    it = _odd_iter() # 初始序列,it是一個奇數序列
    while True:
        n = next(it) # 返回序列的第一個數
        yield n
        # it = filter(_not_divisible(n), it) # 構造新序列
        _test = _not_divisible(n) # 並沒有傳入x的值,所以return x % y 並沒有執行,直接將_f返回給_test
        it = _filter(_test, it)

for m in primes():
    if m < 1000:
        print(m)
    else:
        break

這裡要注意:
filter這個高階函式某種程度上和_filter函式是等價的,參考這裡filter內部實現原理

def _filter(func,seq):
    # filter_list = []
    for s in seq:
        if func(s):
            if( not(func in arrlist)): 
                arrlist.append(func) # 追加程式執行時產生的新的閉包函式
            # filter_list.append(s)
            yield s

_filter執行結果
filter高階函式執行結果

單步除錯新的程式碼:
執行到奇數7
在這裡插入圖片描述
此時arrlist裡的有兩個元素,並且執行兩個不同地址的_not_divisible.閉包函式。
在這裡插入圖片描述
讓我們來看看這裡面是什麼:
在這裡插入圖片描述
在這裡插入圖片描述
雖然看不到arrlist裡面的具體的內容, 但是根據測試結果可以推斷arrlist[0]應該是_not_divisible(3),arrlist[1]應該是_not_divisible(5)。

至於為什麼會遍歷這些閉包函式,我個人的理解是因為 for s in seq 這條語句的關係,同時又因為yield s,s 一直取值為seq序列中的最新值。實際除錯的時候也發現,會在這裡反覆執行,呼叫不同的閉包函式。更深層的東西就不瞭解了。