V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
V2EX 提问指南
toliho
V2EX  ›  问与答

为何 for 内 if 只执行一次?

  •  
  •   toliho · 2020-02-22 21:21:44 +08:00 · 3392 次点击
    这是一个创建于 1743 天前的主题,其中的信息可能已经有所发展或是发生改变。
    例如:names[王八,王九,李大,李二,赵四]
    for a in names:
    if a 姓王,names.remove(a)
    if a 姓李, names.remove(a)
    print(names)
    结果不会留下[赵四],而是有[王九、李二、赵四],为什么会这样?
    for 循环不会把 5 个字符串遍历 5 次吗?
    23 条回复    2020-02-23 19:21:27 +08:00
    BrettD
        1
    BrettD  
       2020-02-22 21:29:41 +08:00 via iPhone
    是不是因为你在 for names 的循环里对 names 本体产生了修改
    wwqgtxx
        2
    wwqgtxx  
       2020-02-22 21:38:26 +08:00 via iPhone   ❤️ 2
    各个语言几乎都不允许在 foreach 语句中修改迭代集合本体,那样会使得迭代器失效
    比如 c++中会导致悬空指针
    python 中导致生成器意外退出
    xupefei
        3
    xupefei  
       2020-02-22 21:45:08 +08:00
    楼上说的对。
    实在不行你就倒着遍历。
    toliho
        4
    toliho  
    OP
       2020-02-22 22:12:35 +08:00
    @wwqgtxx 了解了
    toliho
        5
    toliho  
    OP
       2020-02-22 22:13:50 +08:00
    @xupefei 咋个倒着遍历法? remove 改成 append 吗
    crella
        6
    crella  
       2020-02-22 22:26:40 +08:00
    @wwqgtxx 不一定吧,ruby 的可以在循环里面删,见 https://paste.ubuntu.com/p/YHSsBXpz9c/
    xiri
        7
    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
        8
    xiri  
       2020-02-22 22:32:21 +08:00
    @xiri
    注意这个 range(len(names)),就算后面 len(names)发生变化,对 i 的取值也不会有影响(可以理解成在第一次执行是生成了一个列表,后面是在这个列表中迭代,不会再计算了)
    ybw
        9
    ybw  
       2020-02-22 22:37:02 +08:00 via Android
    不要被楼上半桶水误导了
    比如 c++支持迭代内删除 map、vector 等
    记得事前做一个++操作,保存指向下一元素的指针就好了
    wwqgtxx
        10
    wwqgtxx  
       2020-02-22 22:37:09 +08:00 via iPhone
    @crella 这个吧取决于各个语言的实现,比如 java 的 iterator 要求先调用 next 才允许对旧的 iterator 执行 remove,否则会抛出 IllegalStateException
    至于 ruby 的行为可能要看他的文档才能解释为何可以正常执行了
    不过这种在 foreach 循环中删除元素的行为还是尽量避免的好,容易引起各种奇奇怪怪的问题
    crella
        11
    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
        12
    wwqgtxx  
       2020-02-22 22:41:00 +08:00 via iPhone   ❤️ 1
    @ybw 都说了是在 foreach 中,你要是手动写 for 循环,那么一般是
    if (你的判断条件) it=map.remove(it);
    else it++;
    当然不会有这个破问题

    看别人回答麻烦看仔细,别上来就说别人半桶水
    crella
        13
    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
        14
    crella  
       2020-02-22 23:41:59 +08:00 via Android
    @wwqgtxx 你说的是对的。ruby 遍历数组,修改 array 的同时也影响了 array.each 的抛出的变量。我发的第一段代码只不过是刚好每隔一个数就是偶数而被删除,后面我再测试一下其他的排列,得到和楼主所得的类似的结果。

    不过一般都是直接对数组进行 map 操作,楼主的操作应该也是实验性质的吧。
    crella
        15
    crella  
       2020-02-22 23:59:14 +08:00 via Android
    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
        16
    ik  
       2020-02-23 00:27:49 +08:00 via iPhone
    python 的话是不是这样可解
    for name in names[:]:
    ....
    ddsfeng
        17
    ddsfeng  
       2020-02-23 00:28:46 +08:00
    当王八成立的时候, 你把王八移除了, 而此时 王九 变成了第一个(下标 0),

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

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

    依次类推...

    希望你能明白..
    toliho
        18
    toliho  
    OP
       2020-02-23 16:58:12 +08:00
    哇 一天没来这么多回答,多谢各位
    toliho
        19
    toliho  
    OP
       2020-02-23 16:59:04 +08:00
    @ddsfeng 听懂了
    toliho
        20
    toliho  
    OP
       2020-02-23 17:00:14 +08:00
    @ik 没太懂
    toliho
        21
    toliho  
    OP
       2020-02-23 17:02:08 +08:00
    @crella 我用了这个办法解决:
    new_names =[]
    for n in names:
    if 姓王:
    pass
    elif 姓李:
    pass
    else:
    new_names.append(n)
    toliho
        22
    toliho  
    OP
       2020-02-23 17:05:41 +08:00
    我还发现一个问题,在 for 循环里,如果用了 if if,只会执行第二个 if ;必须用 if elif elif ……或者 if elif else
    ik
        23
    ik  
       2020-02-23 19:21:27 +08:00 via iPhone
    @toliho #20 试一下

    ![20200223191636.png]( https://i.loli.net/2020/02/23/fpmgUsIvdl1BbH9.png)
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   2401 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 25ms · UTC 16:07 · PVG 00:07 · LAX 08:07 · JFK 11:07
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.