做記憶化C++中正確的方法是在混合的Y組合子。
您的基本功能需要修改。它不是直接調用它自己,而是將自身的模板化引用作爲其第一個參數(或者,遞歸作爲其第一個參數)。
我們從Y-combinator開始。然後,我們在operator()
的緩存中添加並將其重命名爲memoizer
,並給它一個固定的簽名(對於表)。
唯一剩下的就是編寫一個tuple_hash<template<class...>class Hash>
,它對元組進行哈希處理。
可以記憶的功能的類型是(((Args...)->R), Args...) -> R
,它使((((Args...) -> R), Args...) -> R) -> ((Args...) -> R)
類型的記憶器成爲可能。使用Y組合器來生成「傳統」遞歸實現也很有用。
請注意,如果memoized函數在調用期間修改了其參數,則記憶器會將結果緩存在錯誤的位置。
struct wrap {};
template<class Sig, class F, template<class...>class Hash=std::hash>
struct memoizer;
template<class R, class...Args, class F, template<class...>class Hash>
struct memoizer<R(Args...), F, Hash> {
using base_type = F;
private:
F base;
std::unordered_map< std::tuple<std::decay_t<Args>...>, R, tuple_hash<Hash> > cache;
public:
template<class... Ts>
R operator()(Ts&&... ts) const
{
auto args = std::make_tuple(ts...);
auto it = cache.find(args);
if (it != cache.end())
return it->second;
auto&& retval = base(*this, std::forward<Ts>(ts)...);
cache.emplace(std::move(args), retval);
return decltype(retval)(retval);
}
template<class... Ts>
R operator()(Ts&&... ts)
{
auto args = std::tie(ts...);
auto it = cache.find(args);
if (it != cache.end())
return it->second;
auto&& retval = base(*this, std::forward<Ts>(ts)...);
cache.emplace(std::move(args), retval);
return decltype(retval)(retval);
}
memoizer(memoizer const&)=default;
memoizer(memoizer&&)=default;
memoizer& operator=(memoizer const&)=default;
memoizer& operator=(memoizer&&)=default;
memoizer() = delete;
template<typename L>
memoizer(wrap, L&& f):
base(std::forward<L>(f))
{}
};
template<class Sig, class F>
memoizer<Sig, std::decay_t<F>> memoize(F&& f) { return {wrap{}, std::forward<F>(f)}; }
live example與基於關閉this SO post硬編碼的散列函數。
auto fib = memoize<size_t(size_t)>(
[](auto&& fib, size_t i)->size_t{
if (i<=1) return 1;
return fib(i-1)+fib(i-2);
}
);
也許[這個答案](http://stackoverflow.com/a/9729954/596781)是你感興趣的。 –
我認爲Sumanth Tambe在他的博客文章中全面處理了這個話題:http://cpptruths.blogspot.nl/2012/01/general-purpose-automatic-memoization.html – sehe
如果這個問題真的是重複的,不應該回答因爲這一個被遷移到它所指的另一個呢? – ascobol