如何以消耗第二个的方式组合 Vec 中的成对项目?

问题描述 投票:0回答:2

我遇到了一个典型的借用检查器错误,我希望人们可以向我展示一些惯用的解决方案。下面请看一个微型示例。

#[derive(Debug)]
pub struct Cell {
    ploidy: u32,
}

impl Cell {
    fn from_ploidy(ploidy: u32) -> Self {
        Cell { ploidy }
    }
    
    fn hybrid(&mut self, cell: Cell) { // I would like `cell` to be consumed.
        self.ploidy += cell.ploidy;
    }
}

fn main() {
    let mut cells = vec![
        Cell::from_ploidy(46), Cell::from_ploidy(40), 
        Cell::from_ploidy(46), Cell::from_ploidy(40)
        ];
    println!("{:?}", cells);

    // To hybrid every two consecutive cells
    // e.g. cells[0].hybrid(cells[1]); 
    //      cells[2].hybrid(cells[3]);
    // and let go cells[1] and cells[3]
    for i in 0..2usize {
        cells[i*2].hybrid(cells[i*2+1]);
    }
    println!("{:?}", cells);
}

错误是

cannot borrow `cells` as immutable because it is also borrowed as mutable
immutable borrow occurs here

基本上我希望将

cells[1]
cells[3]
的资源移至
cells[0]
cells[2]
,同时将
cells[1]
cells[3]
本身从
cells
中释放。

rust vector borrow-checker
2个回答
1
投票

如果您使用

Vec
并且合并的单元格不一定相邻,那么您会遇到一些麻烦。您不能直接从任何索引中提取值,因为
Vec
s 的元素必须始终是连续的。无论是移动元素的补救措施还是使用
swap_remove
从末尾填充,您都会遇到索引对不再稳定的问题。

不用担心,有很多方法可以按照我们喜欢的方式订购东西。理想的情况是对单元格进行排序,使得被消耗的单元格位于末尾,而相应的单元格被合并到开头,这样

cells[idx]
就可以与
cells.pop()
中的单元格合并(因此位于最前面的单元格)结尾应该是相反的顺序)。

我们可以使用我的答案中的代码来创建此排序如何按索引对 Vec 进行排序(重新排序)?并像这样创建排序顺序:

let mut cells = vec![
    Cell::from_ploidy(0), Cell::from_ploidy(1), 
    Cell::from_ploidy(2), Cell::from_ploidy(3),
    Cell::from_ploidy(4), Cell::from_ploidy(5),
];

let pairs = vec![(1, 5), (0, 3), (4, 2)];

let sort_indicies = Iterator::chain(
    pairs.iter().map(|(a, _)| *a),
    pairs.iter().map(|(_, b)| *b).rev(),
).collect();

sort_by_indices(&mut cells, sort_indicies);

然后只需按照我上面概述的步骤合并单元格即可:

for i in 0..cells.len()/2 {
    let cell_to_merge = cells.pop().unwrap();
    cells[i].hybrid(cell_to_merge);
}

我们就完成了!完整的代码和演示输出可在playground获得。您将看到它输出合并的单元格,如

pairs
:

所述
merging 1 with 5
merging 0 with 3
merging 4 with 2

我有一个由 n 个单元组成的向量,其中我随机挑选 k 对(当然,没有重采样),然后对于每一对,将后者融合到前者中,并最终释放后者

如果这应该随机完成,那么我们不需要跟踪我们想要合并的确切对,我们只需打乱数组并只执行最后一步!

use rand::thread_rng;
use rand::seq::SliceRandom;

cells.shuffle(&mut thread_rng());

for i in 0..cells.len()/2 {
    let cell_to_merge = cells.pop().unwrap();
    cells[i].hybrid(cell_to_merge);
}

再次在操场上提供了演示,并且示例输出如下所示合并:

merging 5 with 0 merging 3 with 4 merging 2 with 1
    

1
投票
有多种方法可以实现这一点。最简单的可能是使用

itertools

tuples()
:

use itertools::Itertools; let cells = cells .into_iter() .tuples() .map(|(mut a, b)| { a.hybrid(b); a }) .collect::<Vec<_>>(); println!("{:?}", cells);
    
© www.soinside.com 2019 - 2024. All rights reserved.