关于用埃氏筛选法求素数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)这句,表面看起来似乎等价,执行结果却完全不同。
关于用埃氏筛选法求素数python代码的一些理解_第1张图片
关于用埃氏筛选法求素数python代码的一些理解_第2张图片
很明显,当语句为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)……这些闭包函数。
关于用埃氏筛选法求素数python代码的一些理解_第3张图片
关于用埃氏筛选法求素数python代码的一些理解_第4张图片
关于用埃氏筛选法求素数python代码的一些理解_第5张图片
在这里插入图片描述

为了验证这个想法,我们把程序改下一下:

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

关于用埃氏筛选法求素数python代码的一些理解_第6张图片
关于用埃氏筛选法求素数python代码的一些理解_第7张图片

单步调试新的代码:
执行到奇数7
关于用埃氏筛选法求素数python代码的一些理解_第8张图片
此时arrlist里的有两个元素,并且执行两个不同地址的_not_divisible.闭包函数。
在这里插入图片描述
让我们来看看这里面是什么:
关于用埃氏筛选法求素数python代码的一些理解_第9张图片
关于用埃氏筛选法求素数python代码的一些理解_第10张图片
虽然看不到arrlist里面的具体的内容, 但是根据测试结果可以推断arrlist[0]应该是_not_divisible(3),arrlist[1]应该是_not_divisible(5)。

至于为什么会遍历这些闭包函数,我个人的理解是因为 for s in seq 这条语句的关系,同时又因为yield s,s 一直取值为seq序列中的最新值。实际调试的时候也发现,会在这里反复执行,调用不同的闭包函数。更深层的东西就不了解了。

你可能感兴趣的:(学习笔记)