如何用Rust语言惯用方式构建一个字符计数的哈希表?

7
我想要计算一个字符串中每个字母出现的次数。目标是构建一个HashMap<char,i32>,其中键是字符串中的所有字符,值是出现的次数。
假设我正在循环遍历来自字符串或输入文件的char值。对于每个char,如果它尚未被遇到,则需要将其作为新键添加到HashMap 中,其值为1,但如果它以前已经被遇到,则需要增加该值。
下面是可以工作的代码。请容忍我,我非常新手 Rust:
use std::collections::HashMap;

fn main() {
    let mut letter_counts: HashMap<char,i32> = HashMap::new();

    let input_string = "Hello, world!";
    let char_vec: Vec<char> = input_string.to_lowercase().chars().collect();
    for c in char_vec {
        if let Some(x) = letter_counts.get_mut(&c) {
            *x = *x + 1;
        } else {
            letter_counts.insert(c,1);
        }
    }
    println!("{:?}",letter_counts);
}

我想知道在Rust中是否有一种惯用的方法来做这件事?惯用的意思是是否有一个标准库类型(例如Python的defaultdict)或HashMap上的方法(例如Java的HashMap.computeIfAbsent),可以使这个算法比手工编码更简单、更清晰、更不容易出错?

有一个 frequency_hashmap crate(我没有使用过)。 - John Kugelman
@JohnKugelman,类似的问题,但是提问者接受了一个使代码“更短”而不是惯用的答案。有点像“代码高尔夫”的答案。 - workerjoe
1个回答

12

如果你使用 Entry 接口,可能更符合你想要做的事情,并且更加习惯:

use std::collections::HashMap;

fn main() {
    let mut letter_counts: HashMap<char,i32> = HashMap::new();

    let input_string = "Hello, world!";
    let char_vec: Vec<char> = input_string.to_lowercase().chars().collect();
    for c in char_vec {
        *letter_counts.entry(c).or_insert(0) += 1;
    }
    println!("{:?}",letter_counts);
}

如果该条目不存在,这将允许您创建该条目并同时修改它。

如果您需要更加实用的功能,您可以这样做:

use std::collections::HashMap;

fn main() {
    let input_string = "Hello, world!";
    let letter_counts: HashMap<char, i32> =
        input_string
            .to_lowercase()
            .chars()
            .fold(HashMap::new(), |mut map, c| {
                *map.entry(c).or_insert(0) += 1;
                map
            });
    println!("{:?}", letter_counts);
}

使用折叠来累积项目。

如果您正在寻找一个标准库函数来计算项目的频率,那么没有这样的函数。 函数式方法足够优雅,我个人认为这并不是缺点,并且对于大多数正常的、惯用的 Rust 代码,这是我通常会使用的方法。 在 Rust 中使用迭代器非常普遍。

正如其他人提到的,肯定有更专门针对某些情况的替代方法。


1
“Entry”对我来说是新的。我认为.entry(c).or_insert(0)这一行是我所缺失的关键! - workerjoe

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接