Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit 10e42cb

Browse files
Proposed Casher struct is in directory proposed_edition (#18)
Cacher contribution: memoizes the result of function calls
1 parent 7b0dbd6 commit 10e42cb

File tree

2 files changed

+134
-0
lines changed

2 files changed

+134
-0
lines changed

‎src/caching.rs

Lines changed: 133 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,133 @@
1+
//! Basic Cacher struct which stores a closure and a hashmap.
2+
//! The hasmap stores key value pairs representing previous
3+
//! function calls.
4+
//!
5+
//! When the Cacher function is run, it first does a lookup
6+
//! to see if the value has already been calculated. If it has,
7+
//! it returns that value. If it hasn't, it calculates the value,
8+
//! adds it to the hashmap, and returns it.
9+
10+
use std::collections::HashMap;
11+
12+
/// The Cacher struct (Memoization) stores a function and a Hashmap.
13+
/// The HashMap keeps track of previous input and output for the function so
14+
/// that it only ever has to be called once per input. Use for expensive functions.
15+
pub struct Cacher<F, U, V>
16+
where
17+
F: Fn(U) -> V,
18+
U: std::cmp::Eq + std::hash::Hash + Copy,
19+
V: Copy,
20+
{
21+
calculation: F,
22+
values: HashMap<U, V>,
23+
}
24+
25+
impl<F, U, V> Cacher<F, U, V>
26+
where
27+
F: Fn(U) -> V,
28+
U: std::cmp::Eq + std::hash::Hash + Copy,
29+
V: Copy,
30+
{
31+
/// Constuctor for the Casher
32+
/// # Examples
33+
/// ```
34+
/// # use contest_algorithms::caching::Cacher;
35+
/// let mut squared = Cacher::new(|n: u32| n*n);
36+
/// ```
37+
pub fn new(calculation: F) -> Cacher<F, U, V> {
38+
Cacher {
39+
calculation,
40+
values: HashMap::new(),
41+
}
42+
}
43+
44+
/// Performs a lookup into the HashMap to see if the value has already
45+
/// been calculated. If it has, returns the value. If it has not,
46+
/// calls the function, stores the value, then returns the value
47+
/// # Examples
48+
/// ```
49+
/// # use contest_algorithms::caching::Cacher;
50+
/// let mut squared = Cacher::new(|n: u32| n*n);
51+
///
52+
/// // This is where we call the function
53+
/// let sixteen = squared.call(4);
54+
/// ```
55+
pub fn call(&mut self, arg: U) -> V {
56+
// This is basically the magic of the whole
57+
// structure. You can do this with the entry
58+
// api, but I like how readable this particular
59+
// block of code is.
60+
if let Some(&val) = self.values.get(&arg) {
61+
val
62+
} else {
63+
let val = (self.calculation)(arg);
64+
self.values.insert(arg, val);
65+
val
66+
}
67+
}
68+
69+
/// Calls the function without performing a lookup and replaces
70+
/// the old calculation with the new one, then returns the value
71+
///
72+
/// # Use Case
73+
/// If you're wondering, this is for if some sort of "state" has changed
74+
/// underneath you, so your same function call with the same input
75+
/// might now have different output. For instance, if part of your function
76+
/// reads from a file and
77+
/// you think the contents of that file have changed even though the name
78+
/// has not.
79+
pub fn call_and_replace(&mut self, arg: U) -> V {
80+
let new_val = (self.calculation)(arg);
81+
self.values.insert(arg, new_val);
82+
new_val
83+
}
84+
}
85+
86+
#[cfg(test)]
87+
mod tests {
88+
89+
use super::Cacher;
90+
use std::collections::HashMap;
91+
92+
#[test]
93+
fn test_cacher_basically_works() {
94+
let mut word_len = Cacher::new(|word: &str| word.len());
95+
let hello = word_len.call("hello");
96+
97+
// Test function returns correctly
98+
assert_eq!(hello, 5);
99+
100+
// Test HashMap is correct length
101+
assert_eq!(word_len.values.len(), 1);
102+
103+
// Test HashMap has correct value after one insert
104+
let mut test_map = HashMap::new();
105+
test_map.insert("hello", 5);
106+
assert_eq!(word_len.values, test_map);
107+
108+
// Test HashMap has correct value after duplicate insert
109+
word_len.call("hello");
110+
assert_eq!(word_len.values, test_map);
111+
112+
// Test HashMap has correct values after unique input
113+
word_len.call("wazzup");
114+
test_map.insert("wazzup", 6);
115+
assert_eq!(word_len.values, test_map);
116+
}
117+
118+
#[test]
119+
fn call_and_replace() {
120+
use std::time::Instant;
121+
122+
let mut func = Cacher::new(|_param: usize| Instant::now());
123+
let first_instant = func.call(0);
124+
let lookup_instant = func.call(0);
125+
126+
assert_eq!(first_instant, lookup_instant);
127+
assert_eq!(1, func.values.len());
128+
129+
let second_instant = func.call_and_replace(0);
130+
assert_eq!(1, func.values.len());
131+
assert_ne!(second_instant, lookup_instant);
132+
}
133+
}

‎src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
//! Algorithms Cookbook in Rust.
2+
pub mod caching;
23
pub mod graph;
34
pub mod math;
45
pub mod order;

0 commit comments

Comments
(0)

AltStyle によって変換されたページ (->オリジナル) /