【问题标题】:How to apply a series of iterators to data?如何将一系列迭代器应用于数据?
【发布时间】:2021-12-16 09:37:07
【问题描述】:

我正在通过构建一些基本的遗传学功能来修补 Rust,例如读取包含 DNA 序列的文件,将其转录为 RNA,将其翻译为氨基酸序列等。

我希望这些转换中的每一个都接受和返回迭代器。这样我就可以将它们串在一起(如dna.transcribe().traslate()...)并且只在必要时收集,因此编译器可以优化整个转换链。我是一名来自 Scala/Spark 的数据科学家,所以这种模式很有意义,但我不确定如何在 Rust 中实现它。

我已经阅读了this article about returning iterators,但最终的解决方案似乎是使用 trait 对象(可能会对性能产生很大影响),或者手动滚动带有关联结构的迭代器(这允许我返回一个迭代器,是的,但我不明白它如何让我编写一个也接受迭代器的转换)。

这里有什么一般的架构建议吗?

(仅供参考,到目前为止我的代码是available here,但我觉得我并没有习惯性地使用 Rust,因为 a. 仍然无法完全编译 b. 这种惰性链接操作模式意外导致复杂而凌乱的代码,只能在 Rust nightly 上运行。)

【问题讨论】:

  • 你怎么知道装箱迭代器是一个性能问题——你实际测量过吗?人们倾向于说返回一个装箱的迭代器会影响性能,它确实可以阻止一些优化的发生(例如激进的内联),但请记住,可怕的“动态调度”实际上是一个指针读取 + 跳转。 (这不像 Python 中的 dict 查找。)如果您对每个项目执行任何实质性操作,您将永远不会注意到调度。确保您没有在解决非问题上投入大量精力 - 唯一确定的方法就是衡量。
  • @user4815162342 是的。这就是说我使用迭代器的全部原因是为了提高性能(如我问题的第一段所述)。我想看看将迭代器链接在一起是否比链接接受和返回向量的函数更好(每个函数中都有一个collect)。到目前为止,迭代器模式较慢,即使没有动态调度。

标签: rust iterator


【解决方案1】:

迭代器适配器旨在执行无法以其他方式轻松表达的操作。您的两个示例,.translate().transcribe(),根据您对它们的解释,可以简化为以下内容:

dna
    .map(|x| x.translate())
    .map(|x| x.transcribe())

// or

dna
    .map(|x| x.translate().transcribe())

但是,如果您打算设计自己的迭代器,则以下内容应该可行:

struct Transcriber<I: Iterator<Item = Dna>> {
    inner: I
}

impl<I: Iterator<Item = Dna>> Iterator for Transcriber<I> {
    type Item = TranscribedDna;
    fn next(&mut self) -> Option<Self::Item> {
        self.next().map(|x| x.transcribe())
    }
}

// Extension trait to add the `.transcribe` method to existing iterators
trait TranscribeIteratorExt: Iterator<Item = Dna> {
    fn transcribe(self) -> Transcriber<Self>;
}

impl<I: Iterator<Item = Dna>> TranscriberIteratorExt for I {
    fn transcribe(self) -> Transcriber<Self> {
        Transcriber { inner: self }
    }
}

然后就可以使用了

dna
    .transcribe() // yields TranscribedDna

【讨论】:

  • 感谢您的回答!第一个解决方案不太奏效,因为(正如你可能从 bio 中记得的那样)三个 rna 项目转化为氨基酸,所以你必须做rna.chunks(3).map(translate)。所以基本上我想要一个我可以调用rna.translate() 的糖,并且更普遍地将任何类型的转换封装在一个函数iter -&gt; iter 中。您共享的手动迭代器是有意义的(再次感谢!)并且可以工作,但似乎有很多代码并且只为那个糖工作。我猜这是生锈的唯一方法?
  • 嗯,那么最简洁的表达方式确实是使用.chunks()。这种额外的糖只需要在文件中写入一次并存放起来,迭代器上的所有重要功能都可以通过导入迭代器扩展特征来导入。但是话又说回来,手动迭代器让您可以更明确地处理边缘情况(它比密集迭代器代码中的内联更清晰),例如当您由于逻辑错误而没有获得 3 个 RNA 项时会发生什么。
猜你喜欢
  • 2019-05-22
  • 2019-03-10
  • 1970-01-01
  • 2013-01-16
  • 2019-04-30
  • 2017-01-16
  • 2015-09-08
  • 2010-12-19
  • 2018-06-10
相关资源
最近更新 更多