如何(如果可能)在 Rust 中按值对 BTreeMap 进行排序?

Jon*_*han 5 sorting rust word-frequency

我正在学习一门关于软件安全的课程,其中一项任务是用 Rust 编写一些基本程序。对于其中一项任务,我需要分析一个文本文件并生成几个统计信息。其中之一是生成的文本中十个最常用单词的列表。

我编写了这个程序,除了上面提到的词频统计之外,它执行了作业中的所有任务,程序按照我期望的方式编译和执行:

extern crate regex;

use std::error::Error;
use std::fs::File;
use std::io::prelude::*;
use std::path::Path;
use std::io::BufReader;
use std::collections::BTreeMap;
use regex::Regex;

fn main() {
    // Create a path to the desired file
    let path = Path::new("text.txt");
    let display = path.display();

    let file = match File::open(&path) {
        Err(why) => panic!("couldn't open {}: {}", display,
                           why.description()),
        Ok(file) => file,
    };

    let mut wordcount = 0;
    let mut averagesize = 0;
    let mut wordsize = BTreeMap::new();
    let mut words = BTreeMap::new();

    for line in (BufReader::new(file)).lines() {
        let re = Regex::new(r"([A-Za-z]+[-_]*[A-Za-z]+)+").unwrap();
        for cap in re.captures_iter(&line.unwrap()) {
            let word = cap.at(1).unwrap_or("");
            let lower = word.to_lowercase();
            let s = lower.len();

            wordcount += 1;
            averagesize += s;

            *words.entry(lower).or_insert(0) += 1;
            *wordsize.entry(s).or_insert(0) += 1;
        }
    }

    averagesize = averagesize / wordcount;

    println!("This file contains {} words with an average of {} letters per word.", wordcount, averagesize);

    println!("\nThe number of times a word of a certain length was found.");

    for (size, count) in wordsize.iter() {
        println!("There are {} words of size {}.", count, size);
    }

    println!("\nThe ten most used words.");

    let mut popwords = BTreeMap::new();
    for (word, count) in words.iter() {
        if !popwords.contains_key(count) {
            popwords.insert(count, "");
        }

        let newstring = format!("{} {}", popwords.get(count), word);
        let mut e = popwords.get_mut(count);
    }

    let mut i = 0;
    for (count, words) in popwords.iter() {
        i += 1;
        if i > 10 {
            break;
        }
        println!("{} times: {}", count, words);
    }
}
Run Code Online (Sandbox Code Playgroud)

我有一个BTreeMap(我根据这些说明选择的),words将每个单词存储为关键字,并将其在文本中的相关频率存储为值。此功能按我的预期工作,但我被卡住了。我一直在尝试寻找BTreemap按值排序的方法,或者在 Rust 中找到另一种按值排序的数据结构。

我正在寻找在 Rust 中实现此数据结构(具有频率的单词列表,按频率排序)的正确方法。任何指针都非常感谢!

Luk*_*odt 8

如果您只需要分析静态数据集,最简单的方法是将您的最终转换BTreeMap为 aVec<T>并对后者进行排序(Playground):

use std::iter::FromIterator;

let mut v = Vec::from_iter(map);
v.sort_by(|&(_, a), &(_, b)| b.cmp(&a));
Run Code Online (Sandbox Code Playgroud)

该向量包含(key, value)对作为元组。要对向量进行排序,我们必须使用sort_by()sort_by_key()。为了按降序对向量进行排序,我使用了b.cmp(&a)(而不是a.cmp(&b),这将是自然顺序)。但是还有其他可能来颠倒排序的顺序


但是,如果您确实需要某种数据结构来进行流式计算,则它会变得更加复杂。在这种情况下有很多可能性,但我想使用某种优先级队列可以解决问题。