2013-05-07 90 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();