3

我有一个迭代器,我想在最终调用之前有条件地应用过滤器、跳过等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()

但由于iteris 类型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()

有什么办法可以避免这种重复?

4

2 回答 2

2

在这种情况下,您可以可靠地信任编译器的优化循环不变量。由于should_filter在迭代时不能更改,编译器会发现它可以在循环之前检查该前提条件并跳过测试 if should_filteris 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()
}
于 2021-07-25T20:13:10.157 回答
1

最简单的解决方案,并且可能最接近您的“其他语言”在引擎盖下所做的事情,是将迭代器装箱:

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-26T05:10:19.717 回答