43
43
44
44
/// Performs a lookup into the HashMap to see if the value has already
45
45
/// 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.
47
47
/// # Examples
48
48
/// ```
49
49
/// # use contest_algorithms::caching::Cacher;
@@ -52,30 +52,19 @@ where
52
52
/// // This is where we call the function
53
53
/// let sixteen = squared.call(4);
54
54
/// ```
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.
55
58
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) )
67
61
}
68
62
69
63
/// 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.
79
68
pub fn call_and_replace ( & mut self , arg : U ) -> V {
80
69
let new_val = ( self . calculation ) ( arg) ;
81
70
self . values . insert ( arg, new_val) ;
85
74
86
75
#[ cfg( test) ]
87
76
mod tests {
88
-
89
- use super :: Cacher ;
90
- use std:: collections:: HashMap ;
77
+ use super :: * ;
91
78
92
79
#[ test]
93
80
fn test_cacher_basically_works ( ) {
@@ -116,7 +103,21 @@ mod tests {
116
103
}
117
104
118
105
#[ 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 ( ) {
120
121
use std:: time:: Instant ;
121
122
122
123
let mut func = Cacher :: new ( |_param : usize | Instant :: now ( ) ) ;
0 commit comments