|
| 1 | +//! `bicycle_book_wordcount` はシンプルな文字、単語、行の出現頻度の計数機能を提供します。 |
| 2 | +//! 詳しくは[`count`](fn.count.html)関数のドキュメントを見て下さい。 |
| 3 | +
|
1 | 4 | use regex::Regex; |
2 | 5 | use std::collections::HashMap; |
3 | 6 | use std::io::BufRead; |
4 | 7 |
|
5 | | -pub fn count(input: impl BufRead) -> HashMap<String, usize> { |
| 8 | +/// `input` から1行ずつUTF-8文字列を読み込み、頻度を数える。 |
| 9 | +/// |
| 10 | +/// 頻度を数える対象はオプションによって制御される。 |
| 11 | +/// * [`CountOption::Char`](enum.CountOption.html#variant.Char): Unicodeの1文字毎に頻度を数える |
| 12 | +/// * [`CountOption::Word`](enum.CountOption.html#variant.Word): 正規表現 `\w+` にマッチする単語毎に頻度を数える |
| 13 | +/// * [`CountOption::Line`](enum.CountOption.html#variant.Line): `\n`または`\r\n` で区切られた1行毎に頻度を数える |
| 14 | +/// |
| 15 | +/// # Examples |
| 16 | +/// 入力中の単語の出現頻度を数える例 |
| 17 | +/// |
| 18 | +/// ``` |
| 19 | +/// use std::io::Cursor; |
| 20 | +/// use wcount::{count, CountOption}; |
| 21 | +/// |
| 22 | +/// let mut input = Cursor::new("aa bb cc bb"); |
| 23 | +/// let freq = count(input, CountOption::Word); |
| 24 | +/// |
| 25 | +/// assert_eq!(freq["aa"], 1); |
| 26 | +/// assert_eq!(freq["bb"], 2); |
| 27 | +/// assert_eq!(freq["cc"], 1); |
| 28 | +/// ``` |
| 29 | +/// |
| 30 | +/// # Panics |
| 31 | +/// |
| 32 | +/// 入力がUTF-8でフォーマットされていない場合にパニックする。 |
| 33 | +pub fn count(input: impl BufRead, option: CountOption) -> HashMap<String, usize> { |
6 | 34 | let re = Regex::new(r"\w+").unwrap(); |
7 | 35 | let mut freqs = HashMap::new(); // HashMap<String, usize>型 |
8 | 36 |
|
9 | 37 | for line in input.lines() { |
10 | 38 | let line = line.unwrap(); |
11 | | - for m in re.find_iter(&line) { |
12 | | - let word = m.as_str().to_string(); |
13 | | - // 5. 出現した単語の出現頻度を数える |
14 | | - *freqs.entry(word).or_insert(0) += 1; |
| 39 | + use crate::CountOption::*; |
| 40 | + match option { |
| 41 | + Char => { |
| 42 | + for c in line.chars() { |
| 43 | + *freqs.entry(c.to_string()).or_insert(0) += 1; |
| 44 | + } |
| 45 | + } |
| 46 | + Word => { |
| 47 | + for m in re.find_iter(&line) { |
| 48 | + let word = m.as_str().to_string(); |
| 49 | + // 5. 出現した単語の出現頻度を数える |
| 50 | + *freqs.entry(word).or_insert(0) += 1; |
| 51 | + } |
| 52 | + } |
| 53 | + Line => *freqs.entry(line.to_string()).or_insert(0) += 1, |
15 | 54 | } |
16 | 55 | } |
17 | 56 | freqs |
18 | 57 | } |
| 58 | + |
| 59 | +/// [`count`](fn.count.html)で使うオプション |
| 60 | +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] |
| 61 | +pub enum CountOption { |
| 62 | + /// 文字毎に頻度を数える |
| 63 | + Char, |
| 64 | + /// 単語毎に頻度を数える |
| 65 | + Word, |
| 66 | + /// 行毎に頻度を数える |
| 67 | + Line, |
| 68 | +} |
| 69 | + |
| 70 | +/// オプションのデフォルトは [`Word`](enum.CountOption.html#variant.Word) |
| 71 | +impl Default for CountOption { |
| 72 | + fn default() -> Self { |
| 73 | + CountOption::Word |
| 74 | + } |
| 75 | +} |
| 76 | + |
| 77 | +#[test] |
| 78 | +fn word_count_works() { |
| 79 | + use std::io::Cursor; |
| 80 | + |
| 81 | + let mut exp = HashMap::new(); |
| 82 | + exp.insert("aa".to_string(), 1); |
| 83 | + exp.insert("bb".to_string(), 2); |
| 84 | + exp.insert("cc".to_string(), 1); |
| 85 | + |
| 86 | + assert_eq!(count(Cursor::new("aa bb cc bb"), CountOption::Word), exp); |
| 87 | +} |
| 88 | +#[test] |
| 89 | +fn word_count_works2() { |
| 90 | + use std::io::Cursor; |
| 91 | + |
| 92 | + let mut exp = HashMap::new(); |
| 93 | + exp.insert("aa".to_string(), 1); |
| 94 | + exp.insert("cc".to_string(), 1); |
| 95 | + exp.insert("dd".to_string(), 1); |
| 96 | + |
| 97 | + assert_eq!(count(Cursor::new("aa cc dd"), CountOption::Word), exp); |
| 98 | +} |
0 commit comments