Rust中的惰性序列生成

Rust中的惰性序列生成,rust,lazy-sequences,Rust,Lazy Sequences,如何创建其他语言所称的惰性序列或“生成器”函数 在Python中,我可以使用yield,如以下示例(来自Python的文档)中所示,以不使用中间列表内存的方式惰性地生成一个可编辑的序列: #生成项目而不是返回列表的生成器 def firstn(n): num=0 当num

如何创建其他语言所称的惰性序列或“生成器”函数

在Python中,我可以使用
yield
,如以下示例(来自Python的文档)中所示,以不使用中间列表内存的方式惰性地生成一个可编辑的序列:

#生成项目而不是返回列表的生成器
def firstn(n):
num=0
当num

如何在Rust中执行类似的操作?

Rust 1.0没有生成器功能,因此您必须手动使用

首先,用
next()
方法将Python示例重写为一个类,因为它更接近于在Rust中可能得到的模型。然后,您可以使用实现
迭代器
特性的结构在Rust中重写它

您也可以使用返回闭包的函数来获得类似的结果,但我认为不可能让它实现
迭代器
特性(因为需要调用它来生成新结果)。

Rust确实有生成器,但它们是高度实验性的,目前在稳定锈菌中还没有

在稳定锈1.0及以上的环境中工作 处理您的具体示例。您可以将其与
的语法糖一起使用。

fn main() {
    let sum: u64 = (0..1_000_000).sum();
    println!("{}", sum)
}
如果
范围
不存在怎么办?我们可以创建一个迭代器来建模它

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)
}
由于current Rust中的所有内容都在迭代器上运行,因此我们创建了一个适配器,将生成器转换为迭代器,以便使用更广泛的生态系统。我希望这样的适配器最终会出现在标准库中:

struct GeneratorIteratorAdapter<G>(Pin<Box<G>>);

impl<G> GeneratorIteratorAdapter<G>
where
    G: Generator<Return = ()>,
{
    fn new(gen: G) -> Self {
        Self(Box::pin(gen))
    }
}

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

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

有趣的是,它不如
迭代器
的实现强大。例如,迭代器有一个方法,它允许迭代器的使用者知道还有多少元素。这允许在
收集
装入容器时进行优化。发电机没有任何此类信息。

您可以使用我的stackful Rust,它支持稳定生锈:

#[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);
}
#[宏使用]
外部板条箱发电机;
使用生成器::{generator,Gn};
fn firstn(n:usize)->Generator作为稳定的,您有方便的实用程序。它不是一个协同程序(也就是说,您每次都必须返回),但至少它可以避免您定义另一个结构

from_fn
接受闭包
FnMut()->选项
并重复调用它以创建一个
迭代器

/->铁锈盒2015
fn firstn(n:u64)->impl std::iter::迭代器{
设mut num=0;
标准:国际热核聚变实验堆:从| fn(移动| |{
让结果;
如果num
也有。它不太通用,但可能更易于使用,因为您只需显式地传递种子值。(即,它需要一个初始值
T
和一个后续函数
FnMut(&T)->选项
来创建一个
迭代器

fn firstn(n:u64)->impl std::iter::Iterator{
标准热核实验堆(
一些(0),,
移动|&数值|{
如果num+1
然而,Shepmaster的注释也适用于这些实用程序。(tldr:通常手动滚动的
迭代器
s内存效率更高)


有趣的是,它不如
迭代器
的实现强大。例如,迭代器有一个方法,它允许迭代器的使用者知道还有多少元素。这允许在
收集
装入容器时进行优化。发电机没有任何此类信息


(注意:返回
impl
是Rust 2018的一项功能。有关详细信息,请参阅)

根据确切的顺序,有几个内置迭代器可以使用,例如,或加上a(和/或任何其他组合函数)。(遗憾的是,除了类型之外,还没有其他类型的文档。)我想知道是否有可能编写一个宏,让您定义自定义迭代器结构,但使用较少的样板文件。这个答案非常过时。请参阅“生成器没有任何此类信息”这就是问题所在。生成器的一个主要用例是能够对无限序列执行操作,而不必将无限序列保存在内存中。这种惰性计算有很多应用程序。那么有什么方法可以避免将其转换回迭代器呢?@zer0fool将其转换为迭代器不是问题这里。它仍然可以处理无限序列。迭代器就像一个没有语法糖分的生成器。@zer0fool标准的Rust迭代器已经允许无限序列而不保留整个序列。例如,请参阅。生成器不添加该功能。迭代器有生成器没有的其他信息允许优化。@zer0fool直接使用生成器可以避免转换为迭代器,正如迭代器适配器的主体所示。您将无法在需要迭代器的地方使用它。它可以工作,但并不总是这样。它不是正确的生成器/yieldImagine的最佳替代方法,您需要在返回后返回一些内容你需要睡1秒钟。你不能用
std::iter::from_fn
std::iter::successivers
fn main() {
    let generator_iterator = GeneratorIteratorAdapter::new(firstn(1_000_000));
    let sum: u64 = generator_iterator.sum();
    println!("{}", sum);
}
#[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();