我发现an article包含此代码:
template <typename ReturnType, typename... Args>
std::function<ReturnType (Args...)>
memoize(std::function<ReturnType (Args...)> func)
{
std::map<std::tuple<Args...>, ReturnType> cache;
return ([=](Args... args) mutable {
std::tuple<Args...> t(args...);
if (cache.find(t) == cache.end())
cache[t] = func(args...);
return cache[t];
});
}
你能解释一下吗?我在这里无法理解很多东西,但最奇怪的是缓存是本地的而不是静态的,但也许我错了......
答案 0 :(得分:25)
这是memoization的简单C ++ 1x实现。
memoize
函数返回closure。返回值是一个函数,其状态不同于通过参数传递的状态(在本例中为cache
变量)。
匿名函数中的[=]
位表示返回的函数应该获取所有局部变量的副本。 cache
变量不是静态的,因为它意味着在返回函数的调用之间共享。
因此,每次调用memoize
都会返回一个与自己cache
不同的函数。对memoize
返回的特定闭包的后续调用将从 闭包cache
中插入/获取值。
你可以认为这与更老派的OOP版本相当:
template <typename ReturnType, typename... Args>
class Memoize
{
std::map<std::tuple<Args...>, ReturnType> cache;
public:
ReturnType operator() (Args... args)
{
std::tuple<Args...> t(args...);
if (cache.find(t) == cache.end())
cache[t] = func(args...);
return cache[t];
}
};
答案 1 :(得分:9)
缓存嵌入到lambda本身,并且是本地的。
因此,如果你创建了两个lambda,每个lambda都有一个自己的缓存。
这是实现简单缓存的好方法,因为这样一旦lambda超出范围就会清除所使用的内存,并且你不会爆发内存。
答案 2 :(得分:3)
&#34;这段简单的代码&#34;如果正确调用它,也可以记住递归函数。在这里,我举一个完整的例子:
#include <functional>
#include <iostream>
#include <tuple>
#include <map>
template <typename ReturnType, typename... Args>
std::function<ReturnType (Args...)> memoize(std::function<ReturnType (Args...)> func) {
std::map<std::tuple<Args...>, ReturnType> cache;
return ([=](Args... args) mutable {
std::tuple<Args...> t(args...);
if (cache.find(t) == cache.end())
cache[t] = func(args...);
return cache[t];
});
}
std::function<int (int)> f;
int fib(int n) {
if (n < 2) return n;
return f(n-1) + f(n-2);
}
std::function<int (int, int)> b;
int binomial(int n, int k) {
if (k == 0 || n == k) return 1;
return b(n-1, k) + b(n-1, k-1);
}
int main(void) {
f = memoize(std::function<int (int)>(fib));
std::cout << f(20) << std::endl;
b = memoize(std::function<int (int, int)>(binomial));
std::cout << b(34,15) << std::endl;
}
答案 3 :(得分:2)
引用你发现这个的博客,就在代码下面:
...等号
[=]
表示“捕获 周围的局部变量 按价值范围“,这是必要的 因为我们正在返回lambda 函数和局部变量将 那一刻消失了。
因此,cache
被复制到返回的函数对象中,就像它是成员一样。
(请注意,这段简单的代码将无法记住递归函数。在C ++ 0x中实现fixed-point combinator是留给读者的练习。)
答案 4 :(得分:0)
欢迎来到词汇范围的精彩世界。它可用于创建具有公共和私有成员的整个类型。在函数式语言中,它通常是唯一的方法。
我建议您阅读http://mark-story.com/posts/view/picking-up-javascript-closures-and-lexical-scoping,这是关于Javascript的,但C ++ 0x为C ++添加了相同的概念和(几乎相同的)行为。