微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

如何在第一个 None 值上停止无限迭代器?

如何解决如何在第一个 None 值上停止无限迭代器?

我不知道为什么在下面的代码中,第一次遇到的None(到达空范围0..0后)没有停止迭代

fn main() {
    let iter = (0..)
        .flat_map(move |i| if i < 10 { (0..i).into_iter() } else { 0..0 })
        .fuse();
    for i in iter {
        println!("{}",i);
    }
}

Playground 报告它必须被杀死,为什么它不停止?如何让它停止?

解决方法

flat_map() 是执行此任务的错误工具。它旨在展平迭代器的迭代器,即每当它在内部迭代器上遇到 None 时,它的工作就是切换到下一个迭代器。如果你继续给它提供空迭代器,它会一直循环,直到找到一个非空的迭代器来传递值。 flat_map() 只会在 outer 迭代器执行时终止迭代,在您的情况下永远不会发生。

您需要的是两件事:首先,以除空迭代器以外的其他方式标记迭代结束 - 例如通过使用 Option,其中 Some 表示“这里有另一个迭代器适合您”,而 None 表示“我对迭代不再感兴趣,您可以终止”。其次,您需要在扁平化步骤之前终止迭代,使用具有执行此操作能力的适配器,例如 take_while()scan()

以下是对您的代码的修改,该修改在 i 达到 10 后终止:

fn main() {
    let iter = (0..)
        .map(move |i| if i < 10 { Some(0..i) } else { None })
        .scan((),|_,item| item)
        .flatten();
    for i in iter {
        println!("{}",i);
    }
}

Playground

这里的 flat_map() 分为三个部分:首先是一个 map(),当我们想要迭代时产生 Some(inner_iterator),当我们不再迭代时产生 None。然后是 scan(),它将迭代器转换为 next() 只返回闭包返回值的迭代器。由于闭包返回 item(包裹在选项中的内部迭代器)不变,Some(inner_iterator) 将通过,None 将作为迭代结束信号传播。最后,flatten() 将内部迭代器生成的项目汇集到一个迭代器中,就像 flat_map() 在原始代码中所做的那样。

如果你真的想在第一个空的内部迭代器上终止,也可以通过提取 scan() 中的第一个值并测试它来安排:

// using 1.. to avoid immediately terminating on 0..0
let iter = (1..)
    .map(move |i| if i < 10 { 0..i } else { 0..0 })
    .scan((),mut inner| match inner.next() {
        Some(first) => Some(once(first).chain(inner)),None => None,})
    .flatten();

Playground

请注意,fuse() 不是您想要的。其目的是允许迭代器在耗尽后安全查询。通常,如果您在 next() 已经返回 None 之后调用它,迭代器可能会发生恐慌。 fuse() 扩展迭代器契约以允许在它已经返回 next() 之后调用 None。这是通过内部迭代器的单独标志实现的,并在 next() 中检查该标志。如果您在迭代器返回 None 后不查询它(for 循环不会),那么您就不需要 fuse()

,

来自@user4815462342 的回答,我修改了一些让我更清楚的东西:

    let iter = (0..)
        .map(move |i| if i < 10 { Some(0..i) } else { None })
        .take_while(Option::is_some)
        .flat_map(Option::unwrap);

Playground

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。