2017-02-09 28 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

是的,呃......在沒有可變參數的情況下,實現它我覺得很痛苦(而且體積太大)。 –