Как я могу сохранить векторные элементы с их исходным индексом? - PullRequest
1 голос
/ 05 января 2020

Если у меня есть Vec, я могу перебирать элементы, используя индекс с помощью v.iter().enumerate(), и я могу удалять элементы с помощью v.retain(). Есть ли способ сделать оба сразу?

В этом случае индекс больше не мог бы использоваться для доступа к элементу - это был бы индекс элемента до запуска l oop.

Я могу реализовать это сам, но чтобы быть столь же эффективным, как .retain() Мне нужно было бы использовать unsafe, чего я бы хотел избежать.

Вот результат, который я хочу:

let mut v: Vec<i32> = vec![1, 2, 3, 4, 5, 4, 7, 8];

v.iter()
    .retain_with_index(|(index, item)| (index % 2 == 0) || item == 4);

assert(v == vec![1, 3, 4, 5, 4, 7]);

Ответы [ 3 ]

1 голос
/ 05 января 2020

Я нашел, по сути, тот же вопрос на форуме пользователя Rust . Они предложили это решение, которое не так уж и плохо:

let mut index = 0;
v.retain(|item| {
    index += 1;
    ((index - 1) % 2 == 0) || item == 4
});

В то время это решение не было действительным, потому что порядок итерации retain() не был гарантирован, но, к счастью для меня, кто-то в этом поток задокументировал порядок, так что теперь это так. : -)

1 голос
/ 06 января 2020
Ответы

@ Timmmm и @ Hauleth весьма прагматичны c Я хотел бы предложить несколько альтернатив.

Вот игровая площадка с некоторыми тестами и тестами: https://play.rust-lang.org/?version=nightly&mode=debug&edition=2018&gist=cffc3c39c4b33d981a1a034f3a092e7b

Это уродливо, но если вы действительно хотите метод v.retain_with_index(), вы могли бы сделать немного вставьте копию метода retain с новой чертой:

trait IndexedRetain<T> {
    fn retain_with_index<F>(&mut self, f: F)
    where
        F: FnMut(usize, &T) -> bool;
}

impl<T> IndexedRetain<T> for Vec<T> {
    fn retain_with_index<F>(&mut self, mut f: F)
    where
        F: FnMut(usize, &T) -> bool, // the signature of the callback changes
    {
        let len = self.len();
        let mut del = 0;
        {
            let v = &mut **self;

            for i in 0..len {
                // only implementation change here
                if !f(i, &v[i]) {
                    del += 1;
                } else if del > 0 {
                    v.swap(i - del, i);
                }
            }
        }
        if del > 0 {
            self.truncate(len - del);
        }
    }
}

так, чтобы пример выглядел так:

v.retain_with_index(|index, item| (index % 2 == 0) || item == 4);

Или ... еще лучше, вы могли бы используйте функцию более высокого порядка:

fn with_index<T, F>(mut f: F) -> impl FnMut(&T) -> bool
where
    F: FnMut(usize, &T) -> bool,
{
    let mut i = 0;
    move |item| (f(i, item), i += 1).0
}

, чтобы пример теперь выглядел так:

v.retain(with_index(|index, item| (index % 2 == 0) || item == 4));

(я предпочитаю последнее)

0 голосов
/ 05 января 2020

Если вы хотите перечислить, отфильтровать (сохранить), а затем собрать результирующий вектор, то я бы сказал, чтобы сделать именно это:

v.iter()
    .enumerate()
    .filter(|&(idx, &val)| val - idx > 0)
    .collect()
...