2013-05-07 102 views
20

我怎麼能創造什麼其他語言調用一個懶惰序列或「發電機」的功能?懶惰序列生成拉斯特

在Python,我可以使用yield如在下面的例子中(從Python的文檔)以懶惰地生成的序列,所以可迭代中不使用中介列表的存儲器中的方式:

# a generator that yields items instead of returning a list 
def firstn(n): 
    num = 0 
    while num < n: 
     yield num 
     num += 1 

sum_of_first_n = sum(firstn(1000000)) 

我如何在Rust中做類似的事情?

回答

12

鏽1.0沒有發電機的功能,所以你必須用explicit iterators做手工。

首先,使用next()方法將您的Python示例重寫爲一個類,因爲它更接近Rust中可能獲得的模型。然後你就可以在鏽與實現Iterator性狀的結構重寫。

您可能還可以使用它返回一個閉合來實現類似結果的功能,但我不認爲這將有可能有實現Iterator特質(因爲它需要被調用,生成新的結果)。

+3

取決於精確的順序,也有可能被使用的幾個內置的迭代器例如['Unfoldr'](http://static.rust-lang.org/doc/core/iterator.html#struct-unfoldriterator)或['Counter'](http://static.rust-lang.org/ doc/core/iterator.html#struct-counter)加上['scan'](http://static.rust-lang.org/doc/core/iterator.html#method-scan)(和/或其他組合函數(不幸的是,除了類型之外,還沒有其它文檔)。 – huon 2013-05-08 14:13:20

+0

我想知道是否可以編寫一個宏,它允許您定義自定義迭代器結構,但少一些樣板。 – eremzeit 2017-01-23 11:09:06

18

有發電機,但他們是高度實驗和目前沒有穩定的鏽。

作品在穩定的鏽1.0及以上

Range處理您的具體的例子。

fn main() { 
    let sum: u64 = (0..1_000_000).sum(); 
    println!("{}", sum) 
} 

如果Range不存在:你可以用..的語法糖使用它呢?我們可以創建一個模擬它的迭代器!

struct MyRange { 
    start: u64, 
    end: u64, 
} 

impl MyRange { 
    fn new(start: u64, end: u64) -> MyRange { 
     MyRange { 
      start: start, 
      end: end, 
     } 
    } 
} 

impl Iterator for MyRange { 
    type Item = u64; 

    fn next(&mut self) -> Option<u64> { 
     if self.start == self.end { 
      None 
     } else { 
      let result = Some(self.start); 
      self.start += 1; 
      result 
     } 
    } 
} 

fn main() { 
    let sum: u64 = MyRange::new(0, 1_000_000).sum(); 
    println!("{}", sum) 
} 

膽子是相同的,但比Python版本更明確。值得注意的是,Python的生成器會跟蹤你的狀態。 Rust更喜歡明確性,所以我們必須創建自己的狀態並手動更新它。最重要的部分是Iterator trait的實施。我們指定的迭代器產生一個特定類型(type Item = u64)的值,然後用步進每次迭代,以及如何告訴我們已經達到迭代結束處理。

這個例子是不是真正的Range,它使用泛型強大,但顯示瞭如何去做一個例子。

工程在夜間鏽

每晚鏽does have generators,但他們高度實驗。你需要引入一些不穩定的功能來創建一個。然而,這看起來相當接近Python的例子,有一些特定的防鏽添加劑:

#![feature(generators, generator_trait, conservative_impl_trait)] 

use std::ops::{Generator, GeneratorState}; 

fn firstn(n: u64) -> impl Generator<Yield = u64, Return =()> { 
    move || { 
     let mut num = 0; 
     while num < n { 
      yield num; 
      num += 1; 
     } 
    } 
} 

因爲一切都在目前的鏽上迭代器工作時,我們創造的是,爲了發電機轉換成一個迭代器適配器玩更廣泛的生態系統。我期望這樣的適配器會出現在最終的標準庫:

struct GeneratorIteratorAdapter<G>(G); 

impl<G> Iterator for GeneratorIteratorAdapter<G> 
where 
    G: Generator<Return =()>, 
{ 
    type Item = G::Yield; 

    fn next(&mut self) -> Option<Self::Item> { 
     match self.0.resume() { 
      GeneratorState::Yielded(x) => Some(x), 
      GeneratorState::Complete(_) => None, 
     } 
    } 
} 

現在我們可以使用它:

fn main() { 
    let generator_iterator = GeneratorIteratorAdapter(firstn(1_000_000)); 
    let sum: u64 = generator_iterator.sum(); 
    println!("{}", sum); 
} 

什麼是關於這個有趣的是,它是那麼強大比的實現Iterator。例如,迭代器具有size_hint方法,該方法允許迭代器的使用者瞭解剩餘的元素數量。這允許在collect進入容器時進行優化。發電機沒有任何這樣的信息。

0

你可以用我stackful鏽generator library支持穩定的鏽:

#[macro_use] 
extern crate generator; 
use generator::{Generator, Gn}; 

fn firstn(n: usize) -> Generator<'static,(), usize> { 
    Gn::new_scoped(move |mut s| { 
     let mut num = 0; 
     while num < n { 
      s.yield_(num); 
      num += 1; 
     } 
     done!(); 
    }) 
} 

fn main() { 
    let sum_of_first_n: usize = firstn(1000000).sum(); 
    println!("sum ={}", sum_of_first_n); 
} 

或者更簡單地說:

let n = 100000; 
let range = Gn::new_scoped(move |mut s| { 
    let mut num = 0; 
    while num < n { 
     s.yield_(num); 
     num += 1; 
    } 
    done!(); 
}); 

let sum: usize = range.sum();