首页 文章

Rust中的延迟序列生成

提问于
浏览
21

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

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

3 回答

  • 12

    Rust 1.0没有生成器函数,因此您必须使用explicit iterators手动执行此操作 .

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

    您也可以使用返回闭包的函数来实现类似的结果,但我认为不可能实现 Iterator 特征(因为它需要被调用以生成新结果) .

  • 21

    Rust确实有生成器,但它们是 highly experimental ,目前还没有稳定的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的实施 . 我们指定迭代器产生特定类型的值( type Item = u64 ),然后处理每个迭代的步进以及如何判断我们已经到达迭代的末尾 .

    这个例子没有真正的 Range 那么强大,它使用泛型,但是展示了如何去做它的一个例子 .

    每晚工作Rust

    夜间生锈does have generators,但它们是 highly experimental . 您需要引入一些不稳定的功能来创建一个 . 但是,它看起来非常接近Python示例,并添加了一些特定于Rust的内容:

    #![feature(generators, generator_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;
            }
        }
    }
    

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

    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 unsafe { 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);
    }
    

    's interesting about this is that it'的功能不如 Iterator 的实现 . 例如,迭代器具有size_hint方法,该方法允许迭代器的使用者知道剩余的元素数量 . 这允许在 collect 进入容器时进行优化 . 发电机没有任何此类信息 .

  • 2

    你可以使用我支持稳定Rust的堆栈Rust 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();
    

相关问题