트레이트와 반복자 연습 문서의 문제 3 풀이 코드다.
//! 22 트레이트와 반복자 연습 p3: 글에서 가장 많이 나온 단어 k개를 반복자와 HashMap으로 구한다.
//! 같은 횟수면 사전 순으로 앞선 단어가 먼저다.
//! 변형: 정렬 키에서 단어를 빼면 동점인 단어의 순서가 HashMap 순회 순서를 따라 실행마다 달라지는 것을 확인한다.
//! 의존성: 없음 (표준 라이브러리만)
//! 실행: python3 "_시스템/Rust 계획/rs_check.py" "Rust/4.연습문제/22_traits-iterators-practice_p3.rs"
use std::cmp::Reverse;
use std::collections::HashMap;
fn top_words(text: &str, k: usize) -> Vec<(String, usize)> {
let mut freq: HashMap<String, usize> = HashMap::new();
for w in text
.split(|c: char| !c.is_alphanumeric())
.filter(|w| !w.is_empty())
.map(str::to_lowercase)
{
*freq.entry(w).or_insert(0) += 1;
}
let mut pairs: Vec<(String, usize)> = freq.into_iter().collect();
pairs.sort_by_key(|(w, n)| (Reverse(*n), w.clone()));
pairs.into_iter().take(k).collect()
}
/// 변형: 횟수만 키로 쓴다. 정렬은 안정적이지만, 동점끼리의 처음 순서가 HashMap 순회 순서라 정해지지 않는다.
fn top_words_count_only(text: &str, k: usize) -> Vec<(String, usize)> {
let mut freq: HashMap<String, usize> = HashMap::new();
for w in text
.split(|c: char| !c.is_alphanumeric())
.filter(|w| !w.is_empty())
.map(str::to_lowercase)
{
*freq.entry(w).or_insert(0) += 1;
}
let mut pairs: Vec<(String, usize)> = freq.into_iter().collect();
pairs.sort_by_key(|(_, n)| Reverse(*n));
pairs.into_iter().take(k).collect()
}
fn main() {
let text = "The cat and the dog. And THE bird!";
let top = top_words(text, 3);
println!("{top:?}");
assert_eq!(
top,
[
("the".to_string(), 3),
("and".to_string(), 2),
("bird".to_string(), 1)
]
);
// 변형: 셋째 자리에 오는 단어가 HashMap마다 달라진다(200번 중 두 가지 이상).
let thirds: std::collections::BTreeSet<String> = (0..200)
.map(|_| top_words_count_only(text, 3)[2].0.clone())
.collect();
println!("횟수만 키로 쓸 때 셋째 자리에 나온 단어: {thirds:?}");
assert!(thirds.len() >= 2);
assert!(
thirds
.iter()
.all(|w| ["bird", "cat", "dog"].contains(&w.as_str()))
);
println!("ALL CHECKS PASSED");
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn empty_text() {
assert!(top_words(" ... !!", 3).is_empty());
}
#[test]
fn k_larger_than_words() {
assert_eq!(top_words("a b a", 10).len(), 2);
}
}