我有一个迭代器,在最终调用collect()之前,我希望有条件地对其应用过滤器、跳过等。
在另一种语言中,我可能会这样写
// (Rust-like pseudocode)
let mut iter = [1,2,3,4].iter();
if should_filter {
iter = iter.filter(|x| x % 2 == 0);
}
if should_truncate {
iter = iter.take(2);
}
iter.collect()但是因为iter的类型是Iter,而skip()、filter()返回的类型是Skip、Filter,所以我无法重用iter的原始绑定。因此,我的Rust代码目前看起来像这样:
let iter = [1,2,3,4].iter();
// conditionally filter
if should_filter {
let iter = iter.filter(|x| x % 2 == 0);
// conditionally "truncate"
if should_truncate {
let iter = iter.take(2);
return iter.collect();
}
return iter.collect();
}
// conditionally "truncate"
if should_truncate {
let iter = iter.take(2);
return iter.collect();
}
iter.collect()有什么方法可以避免这种重复吗?
发布于 2021-07-26 13:10:20
最简单的解决方案,也可能是最接近你的“其他语言”在幕后所做的事情,是对迭代器进行装箱:
let mut iter: Box<dyn Iterator<Item = &i32>> = Box::new ([1, 2, 3, 4].iter());
if should_filter {
iter = Box::new (iter.filter(|x| *x % 2 == 0));
}
if should_truncate {
iter = Box::new (iter.take(2));
}
let v: Vec<_> = iter.collect();发布于 2021-07-26 04:13:10
在这种情况下,您可以可靠地信任编译器的优化循环不变量。因为在迭代过程中should_filter不能改变,所以编译器会发现它可以在循环之前检查前提条件,并在should_filter为true时跳过测试。这意味着您可以简单地将条件放入循环中-这看起来效率很低-并拥有更干净的代码。即使检查没有从循环体中删除,CPU的分支预测器也会很容易地跳过它。类似地,您可以“内联”should_truncate条件:
fn do_stuff(
inp: impl IntoIterator<Item = u32>,
should_filter: bool,
should_truncate: bool,
) -> Vec<u32> {
inp.into_iter()
.filter(|x| should_filter && x % 2 == 0)
.take(if should_truncate { 2 } else { usize::MAX })
.collect()
}https://stackoverflow.com/questions/68522044
复制相似问题