簡體   English   中英

我怎樣才能在一個 rust 迭代器中散布一個每 n 個項目的值?

[英]How can I intersperse a rust iterator with a value every n items?

我有一個字符迭代器,我想每 N 個字符添加一個換行符:

let iter = "abcdefghijklmnopqrstuvwxyz".chars();
let iter_with_newlines = todo!();
let string: String = iter_with_newlines.collect();
assert_eq("abcdefghij\nklmnopqrst\nuvwxyz", string);

所以基本上,我想在迭代器中每隔 n 個字符穿插一個換行符。 我怎樣才能做到這一點?

我的一些想法

如果我能做這樣的事情就太好了,其中chunks將是一種將Iterator<T>變成Iterator<Iterator<T>的方法: iter.chunks(10).intersperse('\n').flatten()

如果我能做這樣的事情也會很酷: iter.chunks.intersperseEvery(10, '\n') ,其中intersperseEvery是一種只會散布每 n 個項目的值的方法。

您可以使用enumerateflat_map在沒有臨時分配的情況下做到這一點:

use either::Either;

fn main() {
    let iter = "abcdefghijklmnopqrstuvwxyz".chars();
    let iter_with_newlines = iter
        .enumerate()
        .flat_map(|(i, c)| {
            if i % 10 == 0 {
                Either::Left(['\n', c].into_iter())
            } else {
                Either::Right(std::iter::once(c))
            }
        })
        .skip(1); // The above code add a newline in first position -> skip it
    let string: String = iter_with_newlines.collect();
    assert_eq!("abcdefghij\nklmnopqrst\nuvwxyz", string);
}

操場

如果您不是特別關心性能,您可以使用itertools中的chunks ,將這些塊收集到Vec中,然后將您的元素作為單個元素Vec散布,最終將整個東西展平。

use itertools::Itertools;
iter
    .chunks(3)
    .into_iter()
    .map(|chunk| chunk.collect::<Vec<_>>())
    .intersperse(vec![','])
    .flat_map(|chunk| chunk.into_iter())
    .collect::<String>();

操場

除此之外,考慮編寫自己的迭代器擴展特征,就像 itertools 一樣?

這是我最終做的:

// src/intersperse_sparse.rs

use core::iter::Peekable;

/// An iterator adaptor to insert a particular value
/// every n elements of the adapted iterator.
///
/// Iterator element type is `I::Item`
pub struct IntersperseSparse<I>
where
    I: Iterator,
    I::Item: Clone,
{
    iter: Peekable<I>,
    step_length: usize,
    index: usize,
    separator: I::Item,
}

impl<I> IntersperseSparse<I>
where
    I: Iterator,
    I::Item: Clone,
{
    #[allow(unused)] // Although this function isn't explicitly exported, it is called in the default implementation of the IntersperseSparseAdapter, which is exported.
    fn new(iter: I, step_length: usize, separator: I::Item) -> Self {
        if step_length == 0 {
            panic!("Chunk size cannot be 0!")
        }
        Self {
            iter: iter.peekable(),
            step_length,
            separator,
            index: 0,
        }
    }
}

impl<I> Iterator for IntersperseSparse<I>
where
    I: Iterator,
    I::Item: Clone,
{
    type Item = I::Item;
    fn next(&mut self) -> Option<Self::Item> {
        if self.index == self.step_length && self.iter.peek().is_some() {
            self.index = 0;
            Some(self.separator.clone())
        } else {
            self.index += 1;
            self.iter.next()
        }
    }
}

/// An iterator adaptor to insert a particular value created by a function
/// every n elements of the adapted iterator.
///
/// Iterator element type is `I::Item`
pub struct IntersperseSparseWith<I, G>
where
    I: Iterator,
    G: FnMut() -> I::Item,
{
    iter: Peekable<I>,
    step_length: usize,
    index: usize,
    separator_closure: G,
}

impl<I, G> IntersperseSparseWith<I, G>
where
    I: Iterator,
    G: FnMut() -> I::Item,
{
    #[allow(unused)] // Although this function isn't explicitly exported, it is called in the default implementation of the IntersperseSparseAdapter, which is exported.
    fn new(iter: I, step_length: usize, separator_closure: G) -> Self {
        if step_length == 0 {
            panic!("Chunk size cannot be 0!")
        }
        Self {
            iter: iter.peekable(),
            step_length,
            separator_closure,
            index: 0,
        }
    }
}

impl<I, G> Iterator for IntersperseSparseWith<I, G>
where
    I: Iterator,
    G: FnMut() -> I::Item,
{
    type Item = I::Item;
    fn next(&mut self) -> Option<Self::Item> {
        if self.index == self.step_length && self.iter.peek().is_some() {
            self.index = 0;
            Some((self.separator_closure)())
        } else {
            self.index += 1;
            self.iter.next()
        }
    }
}

/// Import this trait to use the `iter.intersperse_sparse(n, item)` and `iter.intersperse_sparse(n, ||item)` on all iterators.
pub trait IntersperseSparseAdapter: Iterator {
    fn intersperse_sparse(self, chunk_size: usize, separator: Self::Item) -> IntersperseSparse<Self>
    where
        Self: Sized,
        Self::Item: Clone,
    {
        IntersperseSparse::new(self, chunk_size, separator)
    }

    fn intersperse_sparse_with<G>(
        self,
        chunk_size: usize,
        separator_closure: G,
    ) -> IntersperseSparseWith<Self, G>
    where
        Self: Sized,
        G: FnMut() -> Self::Item,
    {
        IntersperseSparseWith::new(self, chunk_size, separator_closure)
    }
}

impl<I> IntersperseSparseAdapter for I where I: Iterator {}

使用它:

// src/main.rs

mod intersperse_sparse;
use intersperse_sparse::IntersperseSparseAdapter;

fn main() {
    let string = "abcdefg";
    let new_string: String = string.chars().intersperse_sparse(3, '\n').collect();
    assert_eq!(new_string, "abc\ndef\ng");
}

使用from_fn構建Iterator

let mut iter = "abcdefghijklmnopqrstuvwxyz".chars().peekable();
let mut count = 0;
let iter_with_newlines = std::iter::from_fn(move || match iter.peek() {
    Some(_) => {
        if count < 10 {
            count += 1;
            iter.next()
        } else {
            count = 0;
            Some('\n')
        }
    }
    None => None,
});
assert_eq!(
    "abcdefghij\nklmnopqrst\nuvwxyz",
    iter_with_newlines.collect::<String>()
);

操場

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM