2017-02-09 49 views
3

一次迭代多个变量可能很有用,重叠(slice::windows),或不是(slice::chunks)。是否有等价于slice :: chunks/windows迭代器来循环对,三元组等?

这只适用于切片;有没有可能为迭代器做到这一点,为了方便使用元组?

像下面这样可以写成:

for (prev, next) in some_iter.windows(2) { 
    ... 
} 

如果没有,可以把它作为对现有迭代器特性来实现?

+2

你可以很容易地做一个'iter_pairs','iter_triples'一旦你决定做什么,如果没有足够的项目在年底,而不是一个通用的“任何规模大小元组”一个有锈此时此刻。 –

+0

如果没有足够的功能,它将不会执行任何操作,例如切片功能。 – ideasman42

+0

这在IRC https://docs.rs/itertools/*/ itertools/trait.Itertools.html#method.tuple_windows'想指向我,并希望在发布答案之前查看它的代码。 – ideasman42

回答

7

TL; DR:有chunkswindows上的任意迭代器/收集最好的办法是先collect它变成一个Vec和迭代


请求的确切语法在Rust中是不可能的。

的问题是,在防锈,一个函数的签名取决于类型,不,虽然依赖打字存在,也有实现它的(很难)几种语言。

这就是为什么chunkswindows顺便返回一个子片; &[T]中的元素数量不是该类型的一部分,因此可以在运行时决定。


让我们假装你问:for slice in some_iter.windows(2)而不是。

这个切片的存储位置在哪里?

它不能住:

  • 原来的集合中,因为一个LinkedList没有一个连续的存储,因为Iterator::Item定义的迭代器
  • ,没有使用寿命可

因此,不幸的是,切片只能在后备存储是切片时使用。


如果动态分配被接受,则有可能使用Vec<Iterator::Item>作为分块的迭代器的Item

struct Chunks<I: Iterator> { 
    elements: Vec<<I as Iterator>::Item>, 
    underlying: I, 
} 

impl<I: Iterator> Chunks<I> { 
    fn new(iterator: I, size: usize) -> Chunks<I> { 
     assert!(size > 0); 

     let mut result = Chunks { 
      underlying: iterator, elements: Vec::with_capacity(size) 
     }; 
     result.refill(size); 
     result 
    } 

    fn refill(&mut self, size: usize) { 
     assert!(self.elements.is_empty()); 

     for _ in 0..size { 
      match self.underlying.next() { 
       Some(item) => self.elements.push(item), 
       None => break, 
      } 
     } 
    } 
} 

impl<I: Iterator> Iterator for Chunks<I> { 
    type Item = Vec<<I as Iterator>::Item>; 

    fn next(&mut self) -> Option<Self::Item> { 
     if self.elements.is_empty() { 
      return None; 
     } 

     let new_elements = Vec::with_capacity(self.elements.len()); 
     let result = std::mem::replace(&mut self.elements, new_elements); 

     self.refill(result.len()); 

     Some(result) 
    } 
} 

fn main() { 
    let v = vec!(1, 2, 3, 4, 5); 

    for slice in Chunks::new(v.iter(), 2) { 
     println!("{:?}", slice); 
    } 
} 

返回结果:

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

这个精明的读者会发现,我偷偷从windows切换到chunks

windows更困难,因为它多次返回相同的元素,要求元素为Clone。此外,由于每次需要返回完整Vec,因此需要在内部保留Vec<Vec<Iterator::Item>>

这是作为练习留给读者。


最后,性能一张纸条:所有分配是会受到伤害(尤其是在windows的情况下)。

最好的分配策略通常是分配一块内存,然后靠它(除非数量真的很大,在这种情况下需要流式传输)。

它在Rust中被称为collect::<Vec<_>>()

而且由于Vecchunkswindows方法(由于实施Deref<Target=[T]>的),你就可以使用,而不是:

for slice in v.iter().collect::<Vec<_>>().chunks(2) { 
    println!("{:?}", slice); 
} 

for slice in v.iter().collect::<Vec<_>>().windows(2) { 
    println!("{:?}", slice); 
} 

有时候最好的解决方案是最简单的。

+0

对不起,downvote,但*所要求的确切语法是不可能在锈*是不正确的;请检查[我的答案](http://stackoverflow.com/a/42139758/155423)。但其余的大部分分析都很有意义。 – Shepmaster

+1

@Shepmaster:你的答案没有要求的确切语法。在some_iter.windows(2)'中,这个请求是'for(prev,next)',其中有一个运行时参数,我认为这意味着我可以传递3并且在for_iter.windows中有for(n0,n1,n2) (3)'那是不可能的。你选择专注于'(prev,next)'并且忽略运行时参数,它可能与OP一致,但就我而言,这不是他们所要求的(我并没有阅读意见)。 –

+0

好点。指定* both *元组的大小和'windows'的参数都没有意义,特别是如果有不匹配的话。我可能会鼓励你在你的回答中明确地注意到这一点 - 可能会增加一个例子? – Shepmaster

5

这是可能的使用Itertools::tuples,由4元组取一个迭代的块:

extern crate itertools;  

use itertools::Itertools; 

fn main() { 
    let some_iter = vec![1, 2, 3, 4, 5, 6].into_iter(); 

    for (prev, next) in some_iter.tuples() { 
     println!("{}--{}", prev, next); 
    } 
} 

playground

1--2 
3--4 
5--6 

除了最多4元组的窗口与Itertools::tuple_windows

extern crate itertools; 

use itertools::Itertools; 

fn main() { 
    let some_iter = vec![1, 2, 3, 4, 5, 6].into_iter(); 

    for (prev, next) in some_iter.tuple_windows() { 
     println!("{}--{}", prev, next); 
    } 
} 

playground

1--2 
2--3 
3--4 
4--5 
5--6 
+0

它可以与3元素的元组一起工作吗?看看这个文档,看起来它可能是可能的。 –

+0

@MatthieuM。是的,但实现的数量*仅限于一个4元组(我已经添加)。 – Shepmaster

+0

是的,呃......在没有可变参数的情况下,实现它我觉得很痛苦(而且体积太大)。 –