context_forge/analysis/
ngrams.rs1#[must_use]
8pub fn extract(tokens: &[String], n: usize) -> Vec<String> {
9 if n == 0 || tokens.len() < n {
10 return Vec::new();
11 }
12
13 tokens.windows(n).map(|window| window.join(" ")).collect()
14}
15
16#[must_use]
18pub fn bigrams(tokens: &[String]) -> Vec<String> {
19 extract(tokens, 2)
20}
21
22#[must_use]
24pub fn trigrams(tokens: &[String]) -> Vec<String> {
25 extract(tokens, 3)
26}
27
28#[cfg(test)]
29mod tests {
30 use super::{bigrams, extract, trigrams};
31
32 fn sample_tokens() -> Vec<String> {
33 ["a", "b", "c", "d"]
34 .into_iter()
35 .map(str::to_string)
36 .collect()
37 }
38
39 #[test]
40 fn test_bigrams() {
41 let tokens = sample_tokens();
42 let expected = vec!["a b".to_string(), "b c".to_string(), "c d".to_string()];
43 assert_eq!(bigrams(&tokens), expected);
44 }
45
46 #[test]
47 fn test_trigrams() {
48 let tokens = sample_tokens();
49 let expected = vec!["a b c".to_string(), "b c d".to_string()];
50 assert_eq!(trigrams(&tokens), expected);
51 }
52
53 #[test]
54 fn test_ngrams_insufficient_tokens() {
55 let tokens = vec!["a".to_string()];
56 assert!(extract(&tokens, 2).is_empty());
57 }
58
59 #[test]
60 fn test_ngrams_exact_size() {
61 let tokens = vec!["a".to_string(), "b".to_string()];
62 assert_eq!(extract(&tokens, 2), vec!["a b".to_string()]);
63 }
64
65 #[test]
66 fn test_unigrams() {
67 let tokens = sample_tokens();
68 assert_eq!(extract(&tokens, 1), tokens);
69 }
70
71 #[test]
72 fn test_zero_gram() {
73 let tokens = sample_tokens();
74 assert!(extract(&tokens, 0).is_empty());
75 }
76}