Разделить итератор на два отдельных итератора

Аналогично этому вопросу: vector">Как мне чередовать два вектора Rust кусками по три в новый вектор?

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

Кроме того, я хочу отделить итератор, не собирая все содержимое исходного итератора.

Вот полупример:

#[derive(Debug)]
struct Foo(i32);

fn main() {
    // note array elements of Foo cannot be copied or cloned
    // v1: [0, 1, 2, 3]
    // v2: [4, 5, 6, 7]
    let v1 = (0..4).map(|num| Foo(num)).collect::<Vec<_>>();
    let v2 = (4..8).map(|num| Foo(num)).collect::<Vec<_>>();

    // generate interleaved iterator
    // interleaved: [0, 1, 4, 5, 2, 3, 6, 7]
    let interleaved = v1.chunks(2)
        .zip(v2.chunks(2))
        .flat_map(|(c1, c2)| c1.iter().chain(c2));
        
    println!("interleaved: {:?}", interleaved.collect::<Vec<_>>());
    
    // regenerate interleaved iterator
    let interleaved = v1.chunks(2)
        .zip(v2.chunks(2))
        .flat_map(|(c1, c2)| c1.iter().chain(c2));
    
    let mut v3: Vec<&Foo> = vec![];
    let mut v4: Vec<&Foo> = vec![];
        
    for (idx, item) in interleaved.enumerate() {
        if idx % 4 < 2 {
            v3.push(item);
        } else {
            v4.push(item);
        }
    }
    
    println!("v3: {:?}, v4: {:?}", v3, v4);
}

Я хотел бы найти идиоматическое решение для отмены чередования итератора без необходимости собирать их ссылки в контейнер.

В более общем плане я хотел бы выяснить, как уменьшить итератор из n элементов до m элементов, где n >= m. В этом случае я хотел бы уменьшить свой чередующийся итератор длиной 8 до итератора с фрагментами 2 длиной 4. Затем итератор с фрагментами будет разделен на два других итератора.

В этом смысле я хочу разобрать свой итератор на куски по 2.


person Saxpy    schedule 11.02.2021    source источник
comment
Представьте, что из двух итераторов, создаваемых этой операцией, вы полностью используете второй, а первый оставляете нетронутым. Это означает, что вы должны полностью использовать исходный (разбитый на части) итератор, но половину элементов все еще нужно где-то хранить (где?). Я не думаю, что вы можете добиться большего успеха, чем ваше решение разделить его на два Vec.   -  person apilat    schedule 12.02.2021


Ответы (1)


Если исходный итератор реализует Clone, то вы можете клонировать его и делать enumerate() + filter() отдельно:

fn uninterleave<T, I: Iterator<Item = T> + Clone>(
    iter: I,
) -> (impl Iterator<Item = T>, impl Iterator<Item = T>) {
    let iter1 = iter
        .clone()
        .enumerate()
        .filter(|(i, _)| i % 4 < 2)
        .map(|(_, value)| value);

    let iter2 = iter
        .enumerate()
        .filter(|(i, _)| i % 4 >= 2)
        .map(|(_, value)| value);

    (iter1, iter2)
}

fn main() {
    let v = vec![0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
    let iter = v.iter();

    let (iter1, iter2) = uninterleave(iter);
    for value in iter1 {
        print!("{} ", value);
    }
    println!();
    for value in iter2 {
        print!("{} ", value);
    }
}

Отпечатки:

0 1 4 5 8 9 
2 3 6 7 
person kmdreko    schedule 12.02.2021
comment
можно ли это сделать без клонирования итератора? Я бы особенно хотел знать, как я могу разделить один итератор на два. Это решение допустимо для клонируемых итераторов, но не будет работать для неклонируемых итераторов. - person Saxpy; 13.02.2021
comment
Я не знаю пути без клонирования или коллекционирования. Для обоих итераторов потребуется изменяемая ссылка на оригинал, что недопустимо. И даже если бы это могло сработать, проблема с созданием двух итераторов из одного такова, как описано в комментарии @apilat: повторение одного приведет к повторению оригинала, что означает, что он может пропустить значения, которые другой итератор не сможет восстановить. Чтобы избежать потери данных, вам нужно знать, какой из них следует опрашивать следующим, что, похоже, не принесет пользы. - person kmdreko; 13.02.2021