栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Python

python:一边由for访问序列,一遍修改序列,出现的问题

Python 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

python:一边由for访问序列,一遍修改序列,出现的问题

一个实例

以一个基础题目:PAT (Basic Level) Practice 1005:继续3n+1,来展现这个问题。
题目的具体描述见PAT网站。

解题思路

1.写入输入的数据后,把要验证的数列转化为数字元素的列表num_list。

2.从头开始遍历num_list,对每个元素num,研究3n+1猜想,而更新num,若更新后的num in num_list,则从num_list中remove它。

3.num_list中只剩下了"关键数",由内置函数sorted排序(reverse=True)。

4.由for遍历sorted_num_list[:-1],print(num, end=" “)。再print(sorted_num_list[-1], end=”")。

解题代码
length = int(input())
s = input()
num_list = [int(n) for n in s.split(" ")]
for num in num_list:
    while num != 1:
        if num % 2 == 0:
            num = num // 2
        else:
            num = (3 * num + 1) // 2
        if num in num_list:
            num_list.remove(num)
sorted_num_list = sorted(num_list, reverse=True)
for n in sorted_num_list[:-1]:
    print(n, end=" ")
print(sorted_num_list[-1], end="")
代码测试

输入:

6
3 5 6 7 8 11

输出:

11 7 6

然而正确的输出是:“7 6”。

出现了什么问题?

按理说,11被7覆盖,在遍历7的时候会被删除。然而并没有。

通过debug研究,发现与for的遍历机制有关–for通过seq的索引i,从小到大(i=0,1,2,…,len(seq)-1)遍历seq:

第一个(i=0)遍历的元素是3,则5,8被从seq中删去,seq变为"3,6,7,11"。

下一个(i=1)遍历的是6,则3被从seq中删去,seq变为"6, 7, 11"。

则下一个(i=2)遍历的是11,则没有元素被删除,seq不变。

而len(seq)-1已经是2,则不再遍历,因此seq不会遍历7,因而11被保留。这样的代码不正确。

另一方面,官方文档中甚至对这个问题有相关建议:

特别地!!我们要注意到图中for遍历copy()那里!!

被遍历的copy并没有因原序列变化而变化,一直是最初的样子–说明程序执行时.copy()函数只执行了最初那一次!然后返回的变量一直用!

但是如果不是遍历seq.copy()而遍历seq,debug测试发现for遍历的序列就是变化的了。

也就是说,for遍历的序列,每访问一个元素,都要读取一次被遍历的序列。若被遍历的是一个函数返回值,则函数只执行最初那一次。

如何解决for带来的问题,并给出上述实例的代码题解?

官方文档已经给出提示:创建另一个序列来辅助。

另外在本题中,我们要做到:若是已被覆盖的数,则不应研究它的"3n+1"。

我们可以用另外一个序列covered_list存已被覆盖的数,
当每访问一个待求证数,则先判断是否in covered_list,决定是否研究它的"3n+1"。
在研究某个数的"3n+1"过程中,将发现的被覆盖的数加入covered_list。
最后排序[n for n in num_list if n not in covered_list]。

代码:

length = int(input())
s = input()
num_list = [int(n) for n in s.split(" ")]
# 新一个list:covered_list来保存已经被“覆盖”的数。如果num not in covered_list,则研究猜想。
covered_list = []
for num in num_list:
    if num not in covered_list:
        while num != 1:
            if num % 2 == 0:
                num = num // 2
            else:
                num = (3 * num + 1) // 2
            if num in num_list:
                covered_list.append(num)
sorted_num_list = sorted([num for num in num_list if num not in covered_list], reverse=True)
for n in sorted_num_list[:-1]:
    print(n, end=" ")
print(sorted_num_list[-1], end="")
意外发现的问题:.copy()与for循环之间另藏的玄机?

我现在还并不明白这是怎么回事–
下面这个代码能够得满分,但并没有成功避开所有被"覆盖"的数,似乎只有首元素被"覆盖"的数避开了研究"3n+1"。

length = int(input())
s = input()
num_list = [int(n) for n in s.split(" ")]
for num in num_list:
    print(num_list)
    print(num)
    while num != 1:
        if num % 2 == 0:
            num = num // 2
        else:
            num = (3 * num + 1) // 2
        if num in num_list:
            num_list.remove(num)
    num_list = num_list.copy()
sorted_num_list = sorted(num_list, reverse=True)
for n in sorted_num_list[:-1]:
    print(n, end=" ")
print(sorted_num_list[-1], end="")

输出(11本被7"覆盖",应该先被remove了,然而仍被访问研究了"3n+1"):

[3, 5, 6, 7, 8, 11]
3
[3, 6, 7, 11]
6
[6, 7, 11]
7
[6, 7]
11
7 6

.copy()明明每次都执行了,然后for只取了原num_list,和第一次的.copy():[3, 6, 7, 11]。

for除了第一次访问序列访问的是原num_list,其他时候都是第一次的copy()。

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/283500.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号