Rust中的惰性序列生成

问题描述 投票:24回答:4

如何创建其他语言称为惰性序列或“生成器”功能的语言?

[在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中做类似的事情?

rust lazy-sequences
4个回答
12
投票

Rust 1.0没有生成器功能,因此您必须使用explicit iterators手动进行。

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

[您也许还可以使用返回闭包的函数来实现类似的结果,但是我认为不可能实现Iterator特性(因为需要调用该特性才能生成一个Range特性)。新结果)。


26
投票

Rust does有生成器,但它们是高度实验性],目前在稳定的Rust中不可用。

在稳定的Rust 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的实现。我们指定迭代器产生特定类型(Iterator)的值,然后处理每次迭代的步骤以及如何判断我们已经到达迭代结束。

此示例不如使用泛型的真实type Item = u64强大,但显示了如何执行此操作的示例。

在每晚Rust中工作

夜锈Range,但它们是高度实验性。您需要引入一些不稳定的功能才能创建一个。但是,它看起来pretty接近Python示例,并带有一些Rust特定的附加内容:

does have generators

由于当前Rust中的所有内容都在迭代器上运行,所以我们创建了一个适配器,该适配器将生成器转换为迭代器,以便与更广泛的生态系统一起使用。我希望这样的适配器最终会出现在标准库中:

#![feature(generators, generator_trait)]

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

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> {
        let me = unsafe { Pin::new_unchecked(&mut self.0) };
        match me.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放入容器时,可以进行优化。生成器没有任何此类信息。


3
投票

您可以使用支持稳定Rust的堆栈式Rust size_hint


0
投票

#[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(); 实用程序。

© www.soinside.com 2019 - 2024. All rights reserved.