Skip to content

Commit a2d9598

Browse files
committed
Cacher with Entry API
1 parent 10e42cb commit a2d9598

File tree

2 files changed

+27
-26
lines changed

2 files changed

+27
-26
lines changed

.travis.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
language: rust
22
rust:
3-
- 1.42.0 # Version currently supported by Codeforces
3+
#- 1.51.0 # Version currently supported by Codeforces
44
- stable
55
- beta
66
- nightly

src/caching.rs

Lines changed: 26 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ where
4343

4444
/// Performs a lookup into the HashMap to see if the value has already
4545
/// been calculated. If it has, returns the value. If it has not,
46-
/// calls the function, stores the value, then returns the value
46+
/// calls the function, stores the value, then returns the value.
4747
/// # Examples
4848
/// ```
4949
/// # use contest_algorithms::caching::Cacher;
@@ -52,30 +52,19 @@ where
5252
/// // This is where we call the function
5353
/// let sixteen = squared.call(4);
5454
/// ```
55+
// TODO: whenever Rust's Entry API gains the ability to take ownership of
56+
// arg only when necessary, this method should follow the same practice.
57+
// Also, Cacher should implement Fn(U)->V once this is possible.
5558
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-
}
59+
let calc = &self.calculation;
60+
*self.values.entry(arg).or_insert_with_key(|&key| calc(key))
6761
}
6862

6963
/// 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.
64+
/// the old return value with the new one, and returns it.
65+
/// Potentially useful if the function reads from a file or RNG
66+
/// whose state may have changed.
67+
// TODO: if there's state, FnMut seems more appropriate.
7968
pub fn call_and_replace(&mut self, arg: U) -> V {
8069
let new_val = (self.calculation)(arg);
8170
self.values.insert(arg, new_val);
@@ -85,9 +74,7 @@ where
8574

8675
#[cfg(test)]
8776
mod tests {
88-
89-
use super::Cacher;
90-
use std::collections::HashMap;
77+
use super::*;
9178

9279
#[test]
9380
fn test_cacher_basically_works() {
@@ -116,7 +103,21 @@ mod tests {
116103
}
117104

118105
#[test]
119-
fn call_and_replace() {
106+
fn test_cacher_speed() {
107+
// Simulate a function that takes 1 second to complete
108+
let mut func = Cacher::new(|x| {
109+
std::thread::sleep(std::time::Duration::from_millis(100));
110+
x * x
111+
});
112+
113+
// Would take 10 minutes without caching
114+
for _ in 0..6000 {
115+
assert_eq!(25, func.call(5));
116+
}
117+
}
118+
119+
#[test]
120+
fn test_call_and_replace() {
120121
use std::time::Instant;
121122

122123
let mut func = Cacher::new(|_param: usize| Instant::now());

0 commit comments

Comments
 (0)