I was inspired by this answer to make this.
ex[0] = 1; ex[n_] := ex[n] = ex[n - 1] + 0.5^n/n!;
The double assignment in the second line is very important. This causes all function calls to be evaluated only once. Once it has been initially evaluated, it will be saved in ex[n].
After a little research, it looks like I just reinvented a memoizer.
#include <unordered_map>
#include <functional>
template < auto callback,
typename data_t,
typename key_t = std::size_t >
class static_memoizer_t
{
public:
using map_t = std::unordered_map< key_t,
data_t >;
using callback_t = decltype( callback );
static_memoizer_t( ) = default;
static_memoizer_t( static_memoizer_t const & ) = default;
static_memoizer_t( static_memoizer_t && ) = default;
static_memoizer_t & operator =( static_memoizer_t const & ) = default;
static_memoizer_t & operator =( static_memoizer_t && ) = default;
~static_memoizer_t( ) = default;
data_t operator[ ]( key_t const & key )
{
if( map.end( ) == map.find( key ) )
return map[ key ] = callback( key );
return map[ key ];
}
data_t operator[ ]( key_t const & key ) const
{
return map.at( key );
}
private:
map_t map;
};
template < typename data_t,
typename key_t = std::size_t >
class dynamic_memoizer_t
{
public:
using map_t = std::unordered_map< key_t,
data_t >;
using callback_t = std::function< data_t( key_t const & ) >;
dynamic_memoizer_t( callback_t callback ):
callback( std::move( callback ) )
{ }
dynamic_memoizer_t( dynamic_memoizer_t const & ) = default;
dynamic_memoizer_t( dynamic_memoizer_t && ) = default;
dynamic_memoizer_t & operator =( dynamic_memoizer_t const & ) = default;
dynamic_memoizer_t & operator =( dynamic_memoizer_t && ) = default;
~dynamic_memoizer_t( ) = default;
data_t operator[ ]( key_t const & key )
{
if( map.end( ) == map.find( key ) )
return map[ key ] = callback( key );
return map[ key ];
}
data_t operator[ ]( key_t const & key ) const
{
return map.at( key );
}
private:
map_t map;
callback_t callback;
};
The basic gist is that it stores the results of expensive functions. I wanted to add support for expensive recursive functions for my initial inspiration, not sure how to approach that though.
Usage:
int square_of( int i ) noexcept
{
return i * i;
}
// static usage
using memoizer_t = static_memoizer_t< square_of, int, int >;
memoizer_t memoizer;
memoizer[ 2 ]; // calculates 2*2, returns 4
memoizer[ 2 ]; // pulls 2*2 from map
// dynamic usage
using memoizer_t = dynamic_memoizer_t< int, int >;
memoizer_t memoizer( square_of );
memoizer[ 2 ]; // calculates 2*2, returns 4
memoizer[ 2 ]; // pulls 2*2 from map
map
vsunordered_map
. \$\endgroup\$