为何 for 内 if 只执行一次?

2020-02-22 21:21:44 +08:00
 toliho
例如:names[王八,王九,李大,李二,赵四]
for a in names:
if a 姓王,names.remove(a)
if a 姓李, names.remove(a)
print(names)
结果不会留下[赵四],而是有[王九、李二、赵四],为什么会这样?
for 循环不会把 5 个字符串遍历 5 次吗?
3418 次点击
所在节点    问与答
23 条回复
BrettD
2020-02-22 21:29:41 +08:00
是不是因为你在 for names 的循环里对 names 本体产生了修改
wwqgtxx
2020-02-22 21:38:26 +08:00
各个语言几乎都不允许在 foreach 语句中修改迭代集合本体,那样会使得迭代器失效
比如 c++中会导致悬空指针
python 中导致生成器意外退出
xupefei
2020-02-22 21:45:08 +08:00
楼上说的对。
实在不行你就倒着遍历。
toliho
2020-02-22 22:12:35 +08:00
@wwqgtxx 了解了
toliho
2020-02-22 22:13:50 +08:00
@xupefei 咋个倒着遍历法? remove 改成 append 吗
crella
2020-02-22 22:26:40 +08:00
@wwqgtxx 不一定吧,ruby 的可以在循环里面删,见 https://paste.ubuntu.com/p/YHSsBXpz9c/
xiri
2020-02-22 22:28:44 +08:00
for a in names
实际执行逻辑类似
for i in range(len(names)):
a=names[i]
第一遍执行,i=0,移除“王八”,names=[王九,李大,李二,赵四]
第二遍执行,i=1,a=李大,移除,names=[王九,李二,赵四]
第三遍,i=2,a=赵四,后面就不用多说了吧
xiri
2020-02-22 22:32:21 +08:00
@xiri
注意这个 range(len(names)),就算后面 len(names)发生变化,对 i 的取值也不会有影响(可以理解成在第一次执行是生成了一个列表,后面是在这个列表中迭代,不会再计算了)
ybw
2020-02-22 22:37:02 +08:00
不要被楼上半桶水误导了
比如 c++支持迭代内删除 map、vector 等
记得事前做一个++操作,保存指向下一元素的指针就好了
wwqgtxx
2020-02-22 22:37:09 +08:00
@crella 这个吧取决于各个语言的实现,比如 java 的 iterator 要求先调用 next 才允许对旧的 iterator 执行 remove,否则会抛出 IllegalStateException
至于 ruby 的行为可能要看他的文档才能解释为何可以正常执行了
不过这种在 foreach 循环中删除元素的行为还是尽量避免的好,容易引起各种奇奇怪怪的问题
crella
2020-02-22 22:38:55 +08:00
@wwqgtxx 别说了,我已经懵 b 了,api doc 也没说明。

https://gitee.com/crella/rubycode/blob/master/drop_from_array.rb

我一会儿去 ruby china 问问
wwqgtxx
2020-02-22 22:41:00 +08:00
@ybw 都说了是在 foreach 中,你要是手动写 for 循环,那么一般是
if (你的判断条件) it=map.remove(it);
else it++;
当然不会有这个破问题

看别人回答麻烦看仔细,别上来就说别人半桶水
crella
2020-02-22 23:19:36 +08:00
楼主现在看我的链接,应该就明白了。我也是想了一会儿。形式可能是:

i = 0
while true
if (i < 数组.长度)
x = 数组[i]
if ....
数组.删除(x)
end
i += 1
else
break
end
end

然后数组.长度是实时更新的
crella
2020-02-22 23:41:59 +08:00
@wwqgtxx 你说的是对的。ruby 遍历数组,修改 array 的同时也影响了 array.each 的抛出的变量。我发的第一段代码只不过是刚好每隔一个数就是偶数而被删除,后面我再测试一下其他的排列,得到和楼主所得的类似的结果。

不过一般都是直接对数组进行 map 操作,楼主的操作应该也是实验性质的吧。
crella
2020-02-22 23:59:14 +08:00
puts RUBY_VERSION
names = %w(王一 王二 王三)
a = names.each
puts a.inspect
names.pop
puts a.inspect


结果
2.5.1
#<Enumerator: ["王一", "王二", "王三"]:each>
#<Enumerator: ["王一", "王二"]:each>

真是奇怪,我以为 enum 会保存自己的值,这里看来并不是
ik
2020-02-23 00:27:49 +08:00
python 的话是不是这样可解
for name in names[:]:
....
ddsfeng
2020-02-23 00:28:46 +08:00
当王八成立的时候, 你把王八移除了, 而此时 王九 变成了第一个(下标 0),

而 迭代器 下次遍历时, 获取 的是下标 1 的, 因为 0 刚才才取过了..

所以此时取到的是 李大...

依次类推...

希望你能明白..
toliho
2020-02-23 16:58:12 +08:00
哇 一天没来这么多回答,多谢各位
toliho
2020-02-23 16:59:04 +08:00
@ddsfeng 听懂了
toliho
2020-02-23 17:00:14 +08:00
@ik 没太懂

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/646674

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX