在 iter().map() 中做不止一件事

Doing more than 1 thing in a iter().map()

我想用地图创建一个新的矢量,但同时,在地图中做其他事情。我正在研究 Advent of Code 2021,第 6 天第 1 部分。

此代码循环遍历向量并将所有值减一。如果该值位于 0,则它将该位置重置为 6 并在向量末尾添加一个 8

fn run_growth_simulation(mut state: Vec<u8>, days: i32) -> usize {
    for _day in 0..days {
        let mut new_fish = 0;
        state.iter_mut().map(|x| match x {
            num: u8 @ 1..=8 => {num - 1},
            0 => {new_fish += 1; 6},
            _ => unreachable!()
        })

        for _fish in 0..new_fish {
            state.push(8);
        }
    }
    state.iter().count() as usize
}

我如何return关闭正确的项目?

我会直接改变迭代器中的值而不是构建一个新数组,因为使用 for_each 而不是 map(或 preferable直接一个for循环)。 然后在 match 语句中改变值:

state.iter_mut().for_each(|x| match x {
             //: u8 removed because it gave me an syntax error
                            // mutate the number directly (we have to use `num` because x was moved)
            num @ 1..=8 => {*num -= 1;},
                                 // mutate the number
            0 => {new_fish += 1; *x = 6;},
            _ => unreachable!()
        });

一种略有不同的方法是计算向量中的 0s,删除它们,将每个值减去 1 并添加新的鱼

作为对此处 for_each() 优于 map() 的回答的补充(因为我们不消耗 map() 发出的内容),下面是一个更简单的示例,试图说明问题(以及为什么借用检查器在禁止此类尝试时是正确的)。 在这两种情况下(test1()test2())我们在扩展向量时迭代它(这就是问题中的意图)。

test1() 中,迭代器在创建时会一次性考虑值的存储。 对于所有后续迭代,它将引用此初始存储,因此此存储不得在此期间移动到内存中的其他位置。 这就是迭代器借用向量的原因(可变与否,这在这里并不重要)。 然而,在这些迭代过程中,我们尝试将新值附加到该向量:这可能会移动存储(用于重新分配目的)并且幸运的是这需要向量的可变借用(然后它被拒绝) .

test2()中,我们避免保留对初始存储的引用,而是使用计数器。 这可行,但这是次优的,因为在每次迭代中,此索引操作 ([]) 都需要检查边界。 前一个函数中的迭代器知道所有边界;这就是迭代器为编译器带来更好优化机会的原因。 请注意,len() 在此处的循环开始时被计算一次;这可能是我们想要的,但如果我们想在每次迭代时重新评估它,那么我们将不得不使用 loop {} 指令。

这里讨论的不是特定于语言而是问题本身。 使用更宽松的编程语言,可能会允许第一次尝试,但会导致内存错误;或者这样的语言应该系统地转向第二次尝试,并在每次迭代时支付边界检查的成本。

最终,您的第二个循环解决方案可能是最佳选择。

fn test1() {
    let mut v = vec![1, 2, 3, 4, 5, 6, 7, 8];
    v.iter_mut().for_each(|e| {
        if *e <= 3 {
            let n = *e + 100;
            // v.push(n) // !!! INCORRECT !!!
            // we are trying to reallocate the storage while iterating over it
        } else {
            *e += 10;
        }
    });
    println!("{:?}", v);
}

fn test2() {
    let mut v = vec![1, 2, 3, 4, 5, 6, 7, 8];
    for i in 0..v.len() {
        let e = &mut v[i];
        if *e <= 3 {
            let n = *e + 100;
            v.push(n);
        } else {
            *e += 10;
        }
    }
    println!("{:?}", v);
}

fn main() {
    test1(); // [1, 2, 3, 14, 15, 16, 17, 18]
    test2(); // [1, 2, 3, 14, 15, 16, 17, 18, 101, 102, 103]
}