Мемоизация фантик функторов в C++
Вот общая оболочка memoization, которую я написал для функций. Он используеттуплехаш .
template<typename R, typename... Args>
class memofunc{
typedef R (*func)(Args...);
func fun_;
unordered_map<tuple<Args...>, R, tuplehash::hash<tuple<Args...> > > map_;
public:
memofunc(func fu):fun_(fu){}
R operator()(Args&&... args){
auto key = make_tuple(std::forward<Args>(args)...);
auto q = map_.find(key);
if(q == map_.end()){
R res = fun_(std::forward<Args>(args)...);
map_.insert({key,res});
return res;
}else{
return q->second;
}
}
};
Пример использования чисел Фибоначчи.
long long fibo(long long x){
static memofunc<long long, long long> memf(fibo);
// try to replace fibo with this new fibo but doesn't work, why?
// function<long long(long long)> fibo = memf;
if(x <= 2) return 1;
// this works but involves changing the original code.
// how to write code such that I dont need to manually add this code in?
return memf(x-1) + memf(x-2);
// old code
// return fibo(x-1) + fibo(x-2);
}
Вопрос в том, что в идеале я мог бы просто добавить несколько строк в начало рекурсивной функции и покончить с запоминанием. Но простая замена не работает, и вот где я застрял.1 ответ:
Ваша проблема, похоже, заключается в том, что вы делаете локальную копию вашего memoizer при каждом вызове функции, а затем уничтожаете ее.
Вот простая версия вашего memoizer с одним аргументом, которая, кажется, работает:
Обратите внимание, что#include <iostream> #include <functional> #include <unordered_map> template<typename Sig, typename F=Sig* > struct memoize_t; template<typename R, typename Arg, typename F> struct memoize_t<R(Arg), F> { F f; mutable std::unordered_map< Arg, R > results; template<typename... Args> R operator()( Args&&... args ) const { Arg a{ std::forward<Args>(args)... }; // in tuple version, std::tuple<...> a auto it = results.find(a); if (it != results.end()) return it->second; R retval = f(a); // in tuple version, use a tuple-to-arg invoker results.emplace( std::forward<Arg>(a), retval ); // not sure what to do here in tuple version return retval; } }; template<typename F> memoize_t<F> memoize( F* func ) { return {func}; } int foo(int x) { static auto mem = memoize(foo); auto&& foo = mem; std::cout << "processing...\n"; if (x <= 0) return foo(x+2)-foo(x+1); // bwahaha if (x <= 2) return 1; return foo(x-1) + foo(x-2);; } int main() { std::cout << foo(10) << "\n"; }
foo(10)
делает только 10 вызововfoo
.Это также допускает:
#define CAT2(A,B,C) A##B##C #define CAT(A,B,C) CAT2(A,B,C) #define MEMOIZE(F) \ static auto CAT( memoize_static_, __LINE__, F ) = memoize(F); \ auto&& F = CAT( memoize_static_, __LINE__, F ) int foo(int x) { MEMOIZE(foo); std::cout << "processing...\n"; if (x <= 0) return 0; if (x <= 2) return 1; return foo(x-1) + foo(x-2);; }
Для людей, которые любят макросы для такого рода вещей.
3-ступенчатая версия может быть лучше.
Во-первых, прелюдия с форвардом объявление функции и оболочки memoizer.
Во-вторых, внутри функции есть псевдоним для имени функции, поэтому рекурсивные вызовы используют функцию запоминания. В-третьих, после объявления функции, псевдоним для имени функции, поэтому внешние вызовы также используют memoized версию. Приведенный выше код запоминает только рекурсивные вызовы, но никогда-начальный вызов.