【问题标题】:How do I process a range in slices in Rust?如何在 Rust 中处理切片中的范围?
【发布时间】:2016-08-30 05:51:18
【问题描述】:

我知道在 Rust 中迭代的首选方法是通过 for var in (range) 语法,但有时我想一次处理该范围内的多个元素。

从 Ruby 的角度来看,我正在尝试在 Rust 中找到一种处理 (1..100).each_slice(5) do |this_slice| 的方法。

我正在尝试类似的事情

for mut segment_start in (segment_size..max_val).step_by(segment_size) {
    let this_segment = segment_start..(segment_start + segment_size).iter().take(segment_size);
}

但我不断收到错误消息,提示我输入了错误的 type 树。这些文档也没有帮助——它们只是不包含这个用例。

Rust 的方法是什么?

【问题讨论】:

    标签: vector rust slice


    【解决方案1】:

    使用chunks(或chunks_mut,如果您需要可变性):

    fn main() {
        let things = [5, 4, 3, 2, 1];
    
        for slice in things.chunks(2) {
            println!("{:?}", slice);
        }
    }
    

    输出:

    [5, 4]
    [3, 2]
    [1]
    

    将其与Range 结合的最简单方法是首先将范围收集到Vec(取消对切片的引用):

    fn main() {
        let things: Vec<_> = (1..100).collect();
    
        for slice in things.chunks(5) {
            println!("{:?}", slice);
        }
    }
    

    另一种纯迭代器解决方案是使用Itertools::chunks_lazy

    extern crate itertools;
    
    use itertools::Itertools;
    
    fn main() {
        for chunk in &(1..100).chunks_lazy(5) {
            for val in chunk {
                print!("{}, ", val);
            }
            println!("");
        }
    }
    

    这提出了一个类似的解决方案,只需要标准库:

    fn main() {
        let mut range = (1..100).peekable();
    
        while range.peek().is_some() {
            for value in range.by_ref().take(5) {
                print!("{}, ", value);
            }
            println!("");
        }
    }
    

    一个技巧是 Ruby 和 Rust 在这里有不同的处理方式,主要集中在效率上。

    在 Ruby 中,Enumerable 可以创建新数组来填充值,而无需担心所有权并每次都返回一个新数组(请与 this_slice.object_id 核对)。

    在 Rust 中,每次都分配一个新向量是非常不寻常的。此外,由于复杂的生命周期问题,您无法轻松返回对迭代器持有的向量的引用。

    一个与 Ruby 非常相似的解决方案是:

    fn main() {
        let mut range = (1..100).peekable();
    
        while range.peek().is_some() {
            let chunk: Vec<_> = range.by_ref().take(5).collect();
    
            println!("{:?}", chunk);
        }
    }
    

    哪些可以被包裹在一个隐藏细节的新迭代器中:

    use std::iter::Peekable;
    
    struct InefficientChunks<I>
        where I: Iterator
    {
        iter: Peekable<I>,
        size: usize,
    }
    
    impl<I> Iterator for InefficientChunks<I>
        where I: Iterator
    {
        type Item = Vec<I::Item>;
    
        fn next(&mut self) -> Option<Self::Item> {
            if self.iter.peek().is_some() {
                Some(self.iter.by_ref().take(self.size).collect())
            } else {
                None
            }
        }
    }
    
    trait Awesome: Iterator + Sized {
        fn inefficient_chunks(self, size: usize) -> InefficientChunks<Self> {
            InefficientChunks {
                iter: self.peekable(),
                size: size,
            }
        }
    }
    
    impl<I> Awesome for I where I: Iterator {}
    
    fn main() {
        for chunk in (1..100).inefficient_chunks(5) {
            println!("{:?}", chunk);
        }
    }
    

    【讨论】:

    • 范围没有块,至少不稳定:error: no method named 'chunks' found for type 'core::ops::Range&lt;usize&gt;' in the current scope
    • @TrevorAlexander 我很抱歉;我读的有点太快了!我添加了一些替代品。您可能想在问题中添加您的目标是什么。根据我的经验,我很少需要一堆数字。可能有更好的整体方法。
    【解决方案2】:

    收集到 vec 很容易影响你的表现。类似于问题中的方法非常好。

    fn chunk_range(range: Range<usize>, chunk_size: usize) -> impl Iterator<Item=Range<usize>> {
        range.clone().step_by(chunk_size).map(move |block_start| {
            let block_end = (block_start + chunk_size).min(range.end);
            block_start..block_end
        })
    }
    
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2015-12-07
      • 1970-01-01
      • 2019-05-16
      • 1970-01-01
      • 1970-01-01
      • 2017-05-18
      • 2012-03-05
      • 1970-01-01
      相关资源
      最近更新 更多