做记忆化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