rust 如何计算迭代器适配器链中已过滤元素的数量

cvxl0en2  于 9个月前  发布在  其他
关注(0)|答案(2)|浏览(65)

我有一个迭代器适配器链(我不知道编译时的数量)应用于初始迭代器。一个简单的例子是通过整除性过滤一个数字范围,并在末尾获得剩余数字的数量:

let n: usize = 10;
    let mut iter_chain: Box<dyn std::iter::Iterator<Item = usize>> = Box::new(1..=n);

    for i in 2..n {
        iter_chain = Box::new(iter_chain.filter(move |j| j % i != 0));
    }

    println!("{}", iter_chain.count());

字符串
现在,我想计数这个过滤器链的每个 * 阶段 * 中的元素的数量。
我的想法是在每个过滤器之间插入一个inspect适配器,它为相应的 stage 增加一个计数器。然而,每个inspect适配器都必须从循环外部的某个地方借用可变计数器。这导致了我在其中存储计数器的Vector的多次相互借用。

let n: usize = 10;
    let mut iter_chain: Box<dyn std::iter::Iterator<Item = usize>> = Box::new(1..=n);

    let mut filter_overview = vec![0; n];
    for i in 2..n {
        // cannot borrow `filter_overview` as mutable more than once at a time
        //                                                   |
        //                                                   V
        iter_chain = Box::new(iter_chain.inspect(|_| filter_overview[i] += 1)); 
        iter_chain = Box::new(iter_chain.filter(move |j| j % i != 0));
    }

    println!("{filter_overview:?} {}", iter_chain.count());


本例中filter_overview的期望值为[10, 5, 3, 3, 2, 2, 1, 1]
有没有解决这个问题的方法,或者我必须使用不同于Vector的东西来存储这些计数器?也许有一种完全不同的方法来实现这一点?
Playground

vsdwdz23

vsdwdz231#

不使用内部可变性,你可以通过迭代filter_overview来做到这一点。但是,你需要小心在iter_chain之前声明它,否则它将在释放后使用:

let n: usize = 10;
let mut filter_overview = vec![0; n];
let mut iter_chain: Box<dyn std::iter::Iterator<Item = usize>> = Box::new(1..=n);

for (i, overview) in std::iter::zip(2..n, &mut filter_overview) {
    iter_chain = Box::new(
        iter_chain
            .inspect(|_| *overview += 1)
            .filter(move |j| j % i != 0),
    );
}

println!("{filter_overview:?} {}", iter_chain.count());

字符串

cwdobuhd

cwdobuhd2#

你可以使用内部可变性或原子来实现这一点。使用原子,向量变成Arc<Vec<AtomicU64>>(或者你选择的任何原子类型)。结果看起来像(Playground link):

use std::sync::{
    atomic::{AtomicU64, Ordering},
    Arc,
};

fn main() {
    let n: usize = 10;
    let mut iter_chain: Box<dyn Iterator<Item = usize>> = Box::new(1..=n);

    // AtomicU64 isn't Clone, so can't use vec![] syntax.
    let filter_overview = Arc::new(Vec::from_iter(
        std::iter::repeat_with(|| AtomicU64::new(0))
            .take(n)
    ));
    
    for i in 2..n {
        // Get a fresh reference by cloning the Arc.
        let overview = filter_overview.clone();
        
        // Use a move closure to move the new Arc into the iterator.
        iter_chain = Box::new(iter_chain.inspect(move |_| {
            overview[i].fetch_add(1, Ordering::Relaxed);
        }));
        
        iter_chain = Box::new(iter_chain.filter(move |j| j % i != 0));
    }

    println!("{filter_overview:?} {}", iter_chain.count());
    println!("Desired output: {:?}", [10, 5, 3, 3, 2, 2, 1, 1]);
}

字符串
输出是

[0, 0, 10, 5, 3, 3, 2, 2, 1, 1] 1
Desired output: [10, 5, 3, 3, 2, 2, 1, 1]

相关问题