一尘不染

如何迭代一次向量,并在此过程中插入/删除/修改多个元素?

algorithm

我想迭代一次数组/向量,并在此途中修改多个元素,因为这是最佳的解决方案。我不想一遍又一遍地扫描它,因为Rust对借贷不满意。

我存储了一个间隔列表,这些间隔表示为[start;stop]有序向量,并且我想添加一个新间隔。它可能是重叠的,所以我想删除不再需要的所有元素。我要一劳永逸。算法看起来像这样(我削减了一些部分):

use std::cmp::{min, max};

#[derive(Debug, PartialEq, Clone, Copy)]
struct Interval {
    start: usize,
    stop: usize,
}

impl Interval {
    fn new(start: usize, stop: usize) -> Interval {
        Interval {
            start: start,
            stop: stop,
        }
    }
    pub fn starts_before_disjoint(&self, other: &Interval) -> bool {
        self.start < other.start && self.stop < other.start
    }
    pub fn starts_before_non_disjoint(&self, other: &Interval) -> bool {
        self.start <= other.start && self.stop >= other.start
    }
    pub fn starts_after(&self, other: &Interval) -> bool {
        self.start > other.start
    }
    pub fn starts_after_disjoint(&self, other: &Interval) -> bool {
        self.start > other.stop
    }
    pub fn starts_after_nondisjoint(&self, other: &Interval) -> bool {
        self.start > other.start && self.start <= other.stop
    }
    pub fn disjoint(&self, other: &Interval) -> bool {
        self.starts_before_disjoint(other)
    }
    pub fn adjacent(&self, other: &Interval) -> bool {
        self.start == other.stop + 1 || self.stop == other.start - 1
    }
    pub fn union(&self, other: &Interval) -> Interval {
        Interval::new(min(self.start, other.start), max(self.stop, other.stop))
    }
    pub fn intersection(&self, other: &Interval) -> Interval {
        Interval::new(max(self.start, other.start), min(self.stop, other.stop))
    }
}

fn main() {
    //making vectors
    let mut vec = vec![
        Interval::new(1, 1),
        Interval::new(2, 3),
        Interval::new(6, 7),
    ];
    let addition = Interval::new(2, 5); // <- this will take over interval @ 2 and will be adjacent to 3, so we have to merge
    let (mut i, len) = (0, vec.len());
    while i < len {
        let r = &mut vec[i];
        if *r == addition {
            return; //nothing to do, just a duplicate
        }
        if addition.adjacent(r) || !addition.disjoint(r) {
            //if they are next to each other or overlapping
            //lets merge
            let mut bigger = addition.union(r);
            *r = bigger;
            //now lets check what else we can merge
            while i < len - 1 {
                i += 1;
                let next = &vec[i + 1];
                if !bigger.adjacent(next) && bigger.disjoint(next) {
                    //nothing to merge
                    break;
                }
                vec.remove(i); //<- FAIL another mutable borrow
                i -= 1; //lets go back
                vec[i] = bigger.union(next); //<- FAIL and yet another borrow
            }
            return;
        }
        if addition.starts_before_disjoint(r) {
            vec.insert(i - 1, addition); // <- FAIL since another refence already borrowed @ let r = &mut vec[i]
        }
        i += 1;
    }
}

由于借款规则,它在几个地方失败了。有没有办法

  1. 一口气用迭代器完成
  2. 解决借款问题

借用拆分功能,但我看不到如何在这里应用。


阅读 170

收藏
2020-07-28

共1个答案

一尘不染

通常,您 不能这样做, 因为这 正是Rust可以防止的一类错误。请检查此事件序列,在该序列中我已i用唯一变量替换,因为编译器仍然不知道将使用什么值。

let r = &mut vec[i1];
let next = &vec[i2];
vec.remove(i3);
vec[i4] = bigger.union(next);         
vec.insert(i5, addition);
  • 如果你删除的任何值 之前, i1i2当你打电话vec.remove(i3),然后在引用nextr将失效,因为所有值将动过。
  • 如果i5i1或之前i2,则将在另一个方向发生相同的情况。
  • 如果i4等于i2,则的 不变next将被更改。
  • 如果i4等于i1,则对的修改r将通过可变引用的唯一所有者以外的其他路径进行。

请注意,它们分别对应于编译器告诉您的要点。

可能一些
这些案件可能是通过非词汇的寿命是固定的,如果编译器变得足够聪明地理解您不再需要有围绕基准。在通过数组索引更改向量的情况下将无济于事;编译器不够聪明,无法跟踪数学并证明您从未碰过“错误”的索引,如果索引是索引,则编译器也不够聪明,无法意识到数组中的两个引用是不相交的。


这种情况下 ,由于您的类型实现了Copy,请利用该类型来获取值。必要时直接写回向量。 如果您从不借贷, 就不会有借贷错误。

fn main() {
    let mut vec = vec![
        Interval::new(1, 1),
        Interval::new(2, 3),
        Interval::new(6, 7),
    ];
    let addition = Interval::new(2, 5);
    let (mut i, len) = (0, vec.len());
    while i < len {
        let r = vec[i];
        if r == addition {
            return;
        }
        if addition.adjacent(&r) || !addition.disjoint(&r) {
            let mut bigger = addition.union(&r);
            vec[i] = bigger;
            while i < len - 1 {
                i += 1;
                let next = vec[i + 1];
                if !bigger.adjacent(&next) && bigger.disjoint(&next) {
                    break;
                }
                vec.remove(i); 
                i -= 1;
                vec[i] = bigger.union(&next);
            }
            return;
        }
        if addition.starts_before_disjoint(&r) {
            vec.insert(i - 1, addition);
        }
        i += 1;
    }
}

实际上,我会按照loganfsmyth的建议进行操作,并将算法更改为采用一片间隔并返回新的Vec。如果您经常这样做,则可以在两个预分配的Vecs之间来回翻转写入,但是到那时,数据结构可能比向量要好得多。也许是一棵间隔树

2020-07-28