1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
//! # function_cache
//!
//! A type that automatically caches the result of a function.

use std::cmp::Eq;
use std::hash::Hash;
use std::collections::HashMap;

/// Wraps an immutable function such that the results of its invocation
/// are cached to a `HashMap<TInput, TReturn>`.
pub struct CachedFunction<TFunction, TInput, TReturn>
where 
    TFunction : Fn(TInput) -> TReturn,
    TInput : Eq + Hash + Copy,
    TReturn: Copy
{
    calculation: TFunction,
    values: HashMap<TInput, TReturn>
}

impl<TFunction, TInput, TReturn> CachedFunction<TFunction, TInput, TReturn>
where 
    TFunction : Fn(TInput) -> TReturn,
    TInput : Eq + Hash + Copy,
    TReturn: Copy
{
    /// Returns a `CachedFunction<TFunction, TInput, TReturn>` that wraps the input function.
    /// 
    /// # Arguments
    /// 
    /// * `function` - The function to wrap in a cache.
    /// 
    /// # Examples
    /// 
    /// ```
    /// use std::thread;
    /// use std::time::Duration;
    /// use function_cache::CachedFunction;
    /// 
    /// let mut cached_function = CachedFunction::new(|x: i32| {
    ///     thread::sleep(Duration::from_secs(2));
    ///     x
    /// });
    /// ```
    pub fn new(function: TFunction) -> CachedFunction<TFunction, TInput, TReturn> {
        CachedFunction {
            calculation: function,
            values: HashMap::new()
        }
    }

    /// Gets the value of evaluating the function with the `arg` as input. First,
    /// the cache is checked and if the value with `arg` has already be computed, it
    /// will be returned from the cache.
    /// 
    /// # Arguments
    /// 
    /// * `arg` - The argument to the function call.
    pub fn value(&mut self, arg: TInput) -> TReturn {
        if self.values.contains_key(&arg) {
            self.values[&arg]
        }
        else {
            let value = (self.calculation)(arg);
            self.values.insert(arg, value);
            value
        }
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn cached_function_value_returns_expected_value() {
        let input = 4;
        let closure = |x: i32| x * x;
        let expected = closure(input);
        let mut function = CachedFunction::new(closure);

        let actual = function.value(input);

        assert_eq!(expected, actual);
    }

    #[test]
    fn cached_function_returns_expected_value_on_subsequent_calls() {
        let input = 4;
        let mut function = CachedFunction::new(|x: i32| x * x);
        let expected = function.value(input);
        let actual = function.value(input);

        assert_eq!(expected, actual);
    }
}