由“素数求和问题”引发的一些思考

在学习Python的for循环和if语句时,碰到了这个案例,记录一下小小心得。

-素数求和问题
为了计算某个自然数以内,所有素数之和。以下程序是一种解决方案。

s=0 
for i in range(2,6):
    for j in range(2,i):
        if(i%j==0):
            break
    else:
        s+=i 
print(s)

程序运行结果输出10,这样解决了6以内素数(即2,3,5)的求和问题。

不过,在学习这段代码的时候,我产生了这样一个疑惑:
当i第一次取值为2的时候,for j in range(2,i)会出现range(2,2)的取值范围。
这样,似乎j只能取值为2,if(2%2==0)判断为真,那么触发break。应该不会执行else语句块,也就不会在最终输出10,而是8(即3+5)才对
想了好久,上百度搜索才明白,问题出在range(2,2)这里:在Python中,这样的取值范围其实为空,并且结束当前循环,去执行一次下面的else。

这里的else并未与if对齐,而是与内层for循环对齐,是一种特殊用法。为了把这个用法搞清楚,我对上面的代码做了微调,如下:

s=0
for i in range(2,6):
    for j in range(2,i):
        if(i%j==0):
            break
    s+=i
print(s)

将else语句删去,直接使用s += i。
运行后,输出为14
原来,这里不使用else的话,s += i 就与内层的for循环成为了并列语句。不管break是否触发,都会执行。即:
1.遇到i为素数时,i%j==0 一直不会成立,break不会触发,内层循环结束后,执行 s += i 。然后回到外层for循环,i增1
2.遇到i为和数时,break触发,结束内层循环,并执行下面的 s += i 语句。然后回到外层for循环,i增1

这样,最终输出的逻辑就是(2+3+4+5)也就是14了。
经小伙伴提醒,既然这时的 s += i 与内层for循环对齐,是并列关系,那么将其放到循环语句上面应该也是一样的效果,即:

s=0
for i in range(2,6):
    s += i
    for j in range(2,i):
        if(i%j==0):
            break
print(s)

运行后,输出为14。猜想正确。进而意识到,二三行已经足够实现2+3+…+(i-1),4-6行是可以注释掉的。即:

s=0
for i in range(2,6):
    s += i
'''
    for j in range(2,i):
        if(i%j==0):
            break
'''
print(s)

运行后,结果仍为14。
经过对比,可以看出,原程序中的else和if虽然没有对齐,但是与break构成了配套的功能,在原程序中,else不是完全与for的并列关系,而是作为break未被触发的奖励语句去执行。在遇到和数时,break被触发,else就同样被忽略了。不会像我更改后的程序那样仍被执行。
这种else的特殊用法是for循环和while循环的一种扩展用法(《Python语言程序设计基础》(嵩天),P110)

进而,我又对原程序做了这样的改动:

s=0
for i in range(2,76):
    for j in range(2,i):
        if(i%j==0):
            break
else:
	s+=i
print(s)

以及这样的改动:

s=0
for i in range(2,76):
    for j in range(2,i):
        if(i%j==0):
            break
s+=i
print(s)

经运行发现,上述两种程序的输出是一致的。都是输出75。
说明在这种缩进下,else语句(或者删去else后的 s += i ) 是与第二行的外层for循环并列,并且else并不与break构成扩展用法(扩展用法指“仅当break不被触发时,才会执行else”)
这也就意味者,else必须与break最近的for循环对齐,才能构成扩展用法。

不过这道题比较特殊的地方在于,原程序中else虽然与break构成扩展用法,理论上应该当且仅当for循环遍历结束,且break未被触发时,执行else语句块。但是i第一次取值为2,range(2,2)时,循环没有遍历而直接结束,触发了else语句块。这算是一种特例吧。

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值