简体   繁体   English

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

[英]How can I iterate a vector once and insert/remove/modify multiple elements along the way?

I want to iterate an array/vector once and modify multiple elements on the way as this is the most optimal solution. 我想迭代一次数组/向量,并在此途中修改多个元素,因为这是最佳的解决方案。 I don't want to scan it again and again just because Rust is unhappy about borrows. 我不想一遍又一遍地扫描它,因为Rust对借贷不满意。

I store a list of intervals represented as [start;stop] in a sorted vector and I want to add a new interval. 我将以[start;stop]表示的间隔列表存储在排序的向量中,我想添加一个新间隔。 It might be overlapping, so I want to remove all elements that are not needed anymore. 它可能是重叠的,所以我想删除不再需要的所有元素。 I want to do it all in one go. 我想一劳永逸。 Algorithm can look something like this (I cut some parts): 算法看起来像这样(我削减了一些部分):

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;
    }
}

It fails in a couple of places because of the borrowing rules. 由于借款规则,它在几个地方失败了。 Is there a way to either 有没有办法

  1. do it with iterators in one go 一口气用迭代器完成
  2. work around the borrowing 解决借款问题

There is borrow splitting available, but I don't see how I can apply it here. 借用拆分功能 ,但我看不到如何在这里应用。

In general, you can't because this is exactly a class of bug that Rust prevents . 通常,您不能这样做,因为这正是Rust可以防止的一类错误 Examine this sequence of events where I've replaced i with unique variables since the compiler doesn't know what values will be used anyway. 检查此事件序列,在该序列中我已用唯一变量替换了i ,因为编译器仍然不知道将使用什么值。

let r = &mut vec[i1];
let next = &vec[i2];
vec.remove(i3);
vec[i4] = bigger.union(next);         
vec.insert(i5, addition);
  • If you remove any value before i1 or i2 when you called vec.remove(i3) , then the references in next and r would be invalidated as all the values would move over. 如果在调用vec.remove(i3)时删除了i1i2 之前的任何值,则nextr的引用将无效,因为所有值都将移动。
  • If i5 is before i1 or i2 , then the same thing would happen, just in the other direction. 如果i5i1i2之前,则将发生同样的事情,只是方向相反。
  • If i4 were equal to i2 , then the immutable value of next would be changed. 如果i4等于i2 ,那么next不变值将被更改。
  • If i4 were equal to i1 , then modifications to r would happen through another path than the single owner of the mutable reference. 如果i4等于i1 ,则对r修改将通过可变引用的单个所有者以外的其他路径进行。

Note how each of these correspond to the points the compiler told you about. 请注意,这些参数如何与编译器告诉您的要点相对应。

It's possible that some of these cases might be fixed via non-lexical lifetimes, if the compiler becomes sufficiently smart to understand that you no longer need to have a reference around. 可能一些案件可能通过非词汇的寿命是固定的,如果编译器变得足够聪明地理解您不再需要有围绕基准。 It will not help with the cases of changing the vector through an array index; 在通过数组索引更改向量的情况下将无济于事; the compiler is not smart enough to track your math and prove that you never touch the "wrong" index, nor will it be smart enough to realize that two references into an array are disjoint if the indices are. 编译器不够聪明,无法跟踪数学并证明您从未碰过“错误的”索引,如果索引是索引,则编译器也不够聪明,无法意识到数组中的两个引用是不相交的。


In this specific case , since your type implements Copy , make use of that to get a value out. 这种特定情况下 ,由于您的类型实现了Copy ,请利用该类型获取值。 Write directly back to the vector when you need to. 必要时直接写回向量。 You can't have borrowing errors if you never borrow . 如果您从不借款,就不会有借款错误。

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;
    }
}

Realistically, I'd do as loganfsmyth suggests and change the algorithm to take a slice of intervals and return a new Vec . 实际上,我会按照loganfsmyth的建议进行操作 ,并将算法更改为采用一片间隔并返回一个新的Vec If you were doing this a lot, you could flip writing back and forth between two pre-allocated Vec s, but at that point there's probably a far better data structure than a vector; 如果您经常这样做,则可以在两个预分配的Vec之间来回翻转写入,但是到那时,数据结构可能比矢量要好得多。 perhaps an interval tree . 也许是一棵间隔树

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 如何使用迭代器在向量中的不同位置插入多个元素? - How to insert multiple elements in the vector at different positions using iterator? 如何删除数组中的“空”元素? - How can I remove "null" elements in an array? 如何使用remove_if从向量中删除pred返回true的所有元素? - How do I remove all elements for which a pred returns true from a vector using remove_if? 在Perl中,如何迭代多个集合的笛卡尔积? - In Perl, how can I iterate over the Cartesian product of multiple sets? 如何调整此算法以处理多次出现的要修改的关键字? - How can I tweak this algorithm to deal with multiple occurrences of a keyword to modify? 如何在带有 X 个元素的 std::vector 中从 p 中提取 n 个元素 - How can i extract n elements out of p in a std::vector with X elements 如何实现对向量的元素顺序添加,在插入之前对其进行排序? - How to realize sequent addition of elements to vector, sorting them before to insert? 如何迭代向量并知道元素的索引? - How do I iterate over a vector and also know the index of the element? 如何在此列表中正确迭代 - How can I iterate properly through this list 如何根据元素的某些属性删除std :: vector的元素? - How would you remove elements of a std::vector based on some property of the elements?
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM