如何对迭代器排序而不将其全部放入向量中?

mam*_*mcx 0 sorting generator rust

我正在构建一个类似于生成器的通用接口,该生成器将数据从一个流传输到另一个流,以最终完成以下操作:

file |> toCsv |> filter |> sort |> filter...
Run Code Online (Sandbox Code Playgroud)

我知道如何对向量/片段进行排序,但是如何将传入的流/迭代器排序而不将其全部放入向量中?

stream.iter().collect_sorted()
Run Code Online (Sandbox Code Playgroud)

我需要融合向量,树,文件,数据库等,因此有时我不知道全部消耗不知道传入数据的大小。

我不反对存储结果。问题是排序与切片/向量有关。我需要能够:

stream.iter().collect_sorted()
Run Code Online (Sandbox Code Playgroud)

代替:

datasource |> Algo.sort |> next...
Run Code Online (Sandbox Code Playgroud)

针对不同的用例存在不同的排序算法,因此最终我希望对现有数据应用最好的排序方法:

let data = datasource |> into_vec
data.sort()
data |> next...
Run Code Online (Sandbox Code Playgroud)

She*_*ter 5

没有所有数据的情况下,实际上不可能对一组值进行排序。例如,如果迭代器有10亿个实例,1后跟一个0,则您根本不知道要先到达零才需要。您可能希望重新熟悉在线和离线算法的概念。

而不是全部放在向量中

这很容易:不要使用向量,请使用任何实现的类型FromIterator。例如,您可以收集到BinaryHeap

use std::{collections::BinaryHeap, iter};

fn main() {
    let a_lot_of_numbers = iter::repeat(1).take(100).chain(iter::once(0));
    let data: BinaryHeap<_> = a_lot_of_numbers.collect();
}
Run Code Online (Sandbox Code Playgroud)

这是否一个好主意完全取决于您的情况。

如果您只是不想看到向量,或者只是想保留链接,则建议使用Itertools::sorted。这在Vec内部使用,这意味着返回第一个值之前,所有数据都存储在内存中

use itertools::Itertools; // 0.8.0
use std::iter;

fn main() {
    let a_lot_of_numbers = iter::repeat(1).take(100).chain(iter::once(0));

    for v in a_lot_of_numbers.sorted() {
        println!("{}", v);
    }
}
Run Code Online (Sandbox Code Playgroud)

这是数据库的常见问题,在其中装载所有数据然后进行排序是不明智的

考虑到折衷的权衡,数据库是非常复杂的软件,需要花费多年的精力。您不会在包管理器中找到这种级别的算法。即使您可以,数据库也不一定总是正确无误,这要求熟练的程序员调整查询以使其性能更好。您需要在Postgres中进行排序的所有知识,涵盖了Postgres可以完成的一系列工作。

从理论上讲,应该有可能编写一个迭代器适配器,该适配器将所有数据写入磁盘,在磁盘上进行排序,然后从磁盘重新读取数据。这称为外部排序

  • @PavelŠimerda 友善地概述了一种算法,该算法将排序“迭代器 [...] 10 亿个 1 实例,后跟一个 0”(为了测试目的,可以随意将十亿减少到一百亿),而无需获取所有值。我非常兴奋地了解到这样的事情是如何可能的。 (3认同)