Как я могу сгруппировать последовательные целые числа в вектор в Rust? - PullRequest
0 голосов
/ 17 мая 2018

У меня есть Vec<i64>, и я хочу знать все группы целых чисел, которые являются последовательными.Как пример:

let v = vec![1, 2, 3, 5, 6, 7, 9, 10];

Я ожидаю что-то подобное или похожее:

[[1, 2, 3], [5, 6, 7], [9, 10]];

Представление (вектор векторов или, может быть, кортежи или что-то еще) действительно не имеет значения, но я должен получить несколько сгруппированных списков с непрерывными числами.

На первый взгляд кажется, что мне нужно использовать itertools и функцию group_by, но я понятия не имею, как ...

Ответы [ 3 ]

0 голосов
/ 17 мая 2018

Вы действительно можете использовать group_by для этого, но вы, возможно, этого не хотите.Вот что я, вероятно, вместо этого написал бы:

fn consecutive_slices(data: &[i64]) -> Vec<&[i64]> {
    let mut slice_start = 0;
    let mut result = Vec::new();
    for i in 1..data.len() {
        if data[i - 1] + 1 != data[i] {
            result.push(&data[slice_start..i]);
            slice_start = i;
        }
    }
    if slice_start > 0 {
        result.push(&data[slice_start..]);
    }
    result
}

Это в принципе похоже на ответ eXodiquas, но вместо накопления Vec<Vec<i64>> я использую индексы для накопления Vec ссылок на срезы, которыеобратитесь к исходным данным.( Этот вопрос объясняет, почему я заставил consecutive_slices взять &[T].)

Также можно сделать то же самое без выделения Vec, возвращая итератор;Тем не менее, мне нравится выше версия лучше.Вот версия с нулевым распределением, которую я придумал:

fn consecutive_slices(data: &[i64]) -> impl Iterator<Item = &[i64]> {
    let mut slice_start = 0;
    (1..data.len() + 1).flat_map(move |i| {
        if i == data.len() || data[i - 1] + 1 != data[i] {
            let begin = slice_start;
            slice_start = i;
            Some(&data[begin..i])
        } else {
            None
        }
    })
}

Она не так удобна для чтения, как цикл for, но ей не нужно выделять Vec для возвращаемого значения, так чтоверсия более гибкая.


Больше для развлечения, чем потому, что я думаю, что это хорошая идея, вот «более функциональная» версия, использующая group_by:

use itertools::Itertools;

fn consecutive_slices(data: &[i64]) -> Vec<Vec<i64>> {
    (&(0..data.len()).group_by(|&i| data[i] as usize - i))
        .into_iter()
        .map(|(_, group)| group.map(|i| data[i]).collect())
        .collect()
}

Идея состоит в том,сделать ключевую функцию для group_by, которая принимает разницу между каждым элементом и его индексом в срезе.Последовательные элементы будут иметь одинаковый ключ, потому что индексы увеличиваются на 1 каждый раз.Одна из причин, по которой я говорю, что это не очень хорошая идея, заключается в том, что довольно сложно получить фрагменты исходной структуры данных;вам почти нужно создать Vec<Vec<i64>> (отсюда два collect s).Другая причина в том, что мне трудно читать.Если вложенная Vec - это то, что вам действительно нужно, ответ eXodiquas , вероятно, является лучшей отправной точкой.

0 голосов
/ 18 мая 2018

Другое возможное решение, в котором используется только стандарт, может быть:

fn consecutive_slices(v: &[i64]) -> Vec<Vec<i64>> {
    let t: Vec<Vec<i64>> = v
        .into_iter()
        .chain([*v.last().unwrap_or(&-1)].iter())
        .scan(Vec::new(), |s, &e| {
            match s.last() {
                None => { s.push(e); Some((false, Vec::new())) },
                Some(&p) if p == e - 1 => { s.push(e); Some((false, Vec::new()))},
                Some(&p) if p != e - 1 => {let o = s.clone(); *s = vec![e]; Some((true, o))},
                _ => None,
            }
         })
         .filter_map(|(n, v)| {
             match n {
                 true => Some(v.clone()),
                 false => None,
             }
         })
         .collect();

    t
}

Цепочка используется для получения последнего вектора.

0 голосов
/ 17 мая 2018
let v = vec![1, 2, 3, 5, 6, 7, 9, 10];
let mut res = Vec::new();
let mut prev = v[0];
let mut sub_v = Vec::new();

sub_v.push(prev);

for i in 1..v.len() {
    if v[i] == prev + 1 {
        sub_v.push(v[i]);
        prev = v[i];
    } else {
        res.push(sub_v.clone());
        sub_v.clear();
        sub_v.push(v[i]);
        prev = v[i];
    }
}

res.push(sub_v);

Это должно решить вашу проблему.

Итерация по данному вектору, проверка, равен ли текущий i64 (в моем случае i32) +1 к предыдущему i64, если это так, вставьте его в вектор (sub_v). После разрыва ряда нажмите sub_v в векторе результатов. Повторите.

Но я думаю, вы хотели что-то функциональное?

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...