计数和分组
掌握常见的基于映射的模式。
计数和分组 是 CoddyKit 上的免费 Learn Rust Coding 课时。 这是第 4 节课,共 4 节。 你可以在下方免费阅读本课时的完整内容 — 然后在浏览器中使用内置代码编辑器和全天候 AI 导师进行实践。 这是 Learn Rust Coding 学习路径的一部分,你的进度在网页和 CoddyKit 应用中同步。 Learn Rust Coding 课程共包含 4 节课。
计数
一个非常常见的任务是统计集合中每个值出现了多少次。
从值映射到计数的 HashMap 是很自然的工具。键是项目,值是它的累计次数。
计数模式
对于每个项目,查找它对应的位置,将默认值设为零,然后加一。
entry API 可以将这些操作简洁地写在一行中。这个模式适用于任何可计数的项目。
use std::collections::HashMap;
fn main() {
let mut counts = HashMap::new();
for n in [1, 1, 2, 3, 3, 3] {
*counts.entry(n).or_insert(0) += 1;
}
println!("threes: {}", counts[&3]);
}统计字符
字符串由字符组成,因此您可以用同样的方式统计字母频率。
使用 chars() 进行迭代,并将每个字符累计到映射中。
use std::collections::HashMap;
fn main() {
let mut freq = HashMap::new();
for c in "banana".chars() {
*freq.entry(c).or_insert(0) += 1;
}
println!("a: {}, n: {}", freq[&'a'], freq[&'n']);
}统计单词
要统计单词,请使用 split_whitespace 按空白字符拆分句子。
每个片段都会成为一个键。这是简单的单词频率分析器的基础。
use std::collections::HashMap;
fn main() {
let text = "go go stop go";
let mut counts = HashMap::new();
for w in text.split_whitespace() {
*counts.entry(w).or_insert(0) += 1;
}
println!("go: {}", counts["go"]);
}查找最大计数
获得计数后,遍历各项并跟踪最高值,即可找出出现频率最高的项目。
您可以将每个条目的计数与目前为止见过的最佳值进行比较。
use std::collections::HashMap;
fn main() {
let mut counts = HashMap::new();
for c in "aabbbc".chars() {
*counts.entry(c).or_insert(0) += 1;
}
let top = counts.iter().max_by_key(|(_, &v)| v).unwrap();
println!("most common: {}", top.0);
}对项目分组
分组会将具有相同属性的项目放入同一组。
使用从分组键映射到成员 Vec 的 HashMap。entry API 在首次使用时会将默认值设为空向量。
use std::collections::HashMap;
fn main() {
let mut groups: HashMap<bool, Vec<i32>> = HashMap::new();
for n in 1..=6 {
groups.entry(n % 2 == 0).or_default().push(n);
}
println!("evens: {:?}", groups[&true]);
}按首字母分组
一种自然的分组键是单词的第一个字符。
提取该字符,然后将每个单词推入相应的分组中。
use std::collections::HashMap;
fn main() {
let words = ["apple", "avocado", "banana"];
let mut by_letter: HashMap<char, Vec<&str>> = HashMap::new();
for w in words {
let first = w.chars().next().unwrap();
by_letter.entry(first).or_default().push(w);
}
println!("a-words: {}", by_letter[&'a'].len());
}按长度分组
您可以按照任意计算出的属性进行分组,例如每个单词的长度。
这里的键是一个数字,每个分组会收集具有该长度的单词。
use std::collections::HashMap;
fn main() {
let words = ["hi", "yo", "hey", "hello"];
let mut by_len: HashMap<usize, Vec<&str>> = HashMap::new();
for w in words {
by_len.entry(w.len()).or_default().push(w);
}
println!("len 2: {}", by_len[&2].len());
}统计唯一项目
要统计不重复的值,可以将集合与映射结合使用。或者直接收集到 HashSet 中,再读取其长度。
这样就能得到不同项目的数量,而不会重复计算相同项目。
use std::collections::HashSet;
fn main() {
let visits = ["sam", "mia", "sam", "leo", "mia"];
let unique: HashSet<&str> = visits.iter().copied().collect();
println!("unique visitors: {}", unique.len());
}报告结果
完成计数或分组后,遍历映射以输出报告。
请记住,顺序未作规定;如果需要稳定的输出,请先对键进行排序。
use std::collections::HashMap;
fn main() {
let mut counts = HashMap::new();
for c in "abca".chars() {
*counts.entry(c).or_insert(0) += 1;
}
let mut keys: Vec<_> = counts.keys().collect();
keys.sort();
for k in keys {
println!("{}: {}", k, counts[k]);
}
}按组求和
除了收集成员,您还可以进行聚合,例如按键对值求和。
将位置的默认值设为零,然后加上每个项目的值,就像计数一样,只不过这里处理的是实际数值。
use std::collections::HashMap;
fn main() {
let sales = [("a", 10), ("b", 5), ("a", 3)];
let mut totals: HashMap<&str, i32> = HashMap::new();
for (k, amount) in sales {
*totals.entry(k).or_insert(0) += amount;
}
println!("a total: {}", totals["a"]);
}快速检查
您希望按照单词的首字母将单词分组到不同分组中。
回顾
您使用 entry 模式统计了项目、字符和单词,并找出了出现频率最高的值。
您按字母和长度将项目分到 Vec 分组中,并使用 HashSet 统计了不重复的值。
常见问题解答
「计数和分组」课时是免费的吗?
是的 — 「计数和分组」的完整文本可在网页上免费阅读。要进行交互式练习(内置代码编辑器和全天候 AI 导师)并解锁 Learn Rust Coding 课程的其余内容,请升级到 CoddyKit PRO。 Learn Rust Coding 课程共包含 4 节课。
「计数和分组」这节课中我会学到什么?
掌握常见的基于映射的模式。 你通过在浏览器中直接运行的动手代码来练习 Learn Rust Coding,全天候 AI 导师会在你学习这节课的过程中回答你的问题。
学习 Learn Rust Coding 需要有经验吗?
无需任何先前经验。CoddyKit 上的 Learn Rust Coding 课程适合初学者到高级学习者,你可以从这里开始或从头开始,按照自己的节奏学习。 这是第 4 节课,共 4 节。
「计数和分组」课时需要多长时间?
大多数 CoddyKit 课程大约需要 5–10 分钟。每节课都很精短且互动,所以你能稳步进步,并在网页和应用中从离开的地方继续。
我能在这节 Learn Rust Coding 课中编写并运行代码吗?
能。每节 Learn Rust Coding 课都包含内置代码编辑器,你可以在浏览器中直接编写并运行真实代码,并获得即时 AI 反馈 — 无需本地设置。