如何在第一个 None 值上停止无限迭代器?
How to stop an infinite iterator on the first None value?
我不知道为什么在下面的代码中,在第一次遇到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()
or 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);
}
}
这里 flat_map()
分为三个部分:首先是 map()
,当我们想要迭代时产生 Some(inner_iterator)
,当我们不再这样做时产生 None
。然后 scan()
将迭代器转换为 next()
仅 returns 闭包返回值的迭代器。由于闭包 returns 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();
请注意,fuse()
不是您想要的。它的目的是让迭代器在耗尽后能够被安全地查询。通常,如果您在 next()
已经返回 None
之后调用它,迭代器可能会崩溃。 fuse()
扩展迭代器契约以允许在返回 None
后调用 next()
。这是通过沿内部迭代器的单独标志实现的,并在 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);
我不知道为什么在下面的代码中,在第一次遇到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()
or 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);
}
}
这里 flat_map()
分为三个部分:首先是 map()
,当我们想要迭代时产生 Some(inner_iterator)
,当我们不再这样做时产生 None
。然后 scan()
将迭代器转换为 next()
仅 returns 闭包返回值的迭代器。由于闭包 returns 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();
请注意,fuse()
不是您想要的。它的目的是让迭代器在耗尽后能够被安全地查询。通常,如果您在 next()
已经返回 None
之后调用它,迭代器可能会崩溃。 fuse()
扩展迭代器契约以允许在返回 None
后调用 next()
。这是通过沿内部迭代器的单独标志实现的,并在 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);