当前位置:  开发笔记 > 编程语言 > 正文

Rust中的延迟序列生成

如何解决《Rust中的延迟序列生成》经验,为你挑选了2个好方法。

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

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



1> Shepmaster..:

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

这个例子不像Range使用泛型的真实那样强大,但是展示了如何去做它的例子.

适用于夜间生锈

Nightly Rust 确实有发电机,但它们具有很高的实验性.您需要引入一些不稳定的功能来创建一个.但是,它看起来非常接近Python示例,并添加了一些特定于Rust的内容:

#![feature(generators, generator_trait)]

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

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

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

struct GeneratorIteratorAdapter(G);

impl Iterator for GeneratorIteratorAdapter
where
    G: Generator,
{
    type Item = G::Yield;

    fn next(&mut self) -> Option {
        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方法,该方法允许迭代器的使用者知道剩余的元素数量.这允许在collect进入容器时进行优化.发电机没有任何此类信息.



2> Lucian..:

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

首先,重写你的Python举例来说,如一个类next()的方法,因为这是更接近你很可能会生锈获取模型.然后,您可以使用实现Iterator特征的结构在Rust中重写它.

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


根据确切的顺序,有几个可以使用的内置迭代器,例如[`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)(和/或任何其他组合函数.(不幸的是,除了类型之外没有其他文档.)
推荐阅读
sx-March23
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有