这个 C++11 代码(memoize)有什么作用?

发布于 2024-10-22 23:19:54 字数 664 浏览 7 评论 0原文

我发现一篇文章包含此代码:

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];
    });
}

你能解释一下吗请问这个?我在这里无法理解很多事情,但最奇怪的是缓存是本地的而不是静态的,但也许我错了......

I found an article that contains this code:

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];
    });
}

Can you explain this please? I can't understand many things here, but the weirdest thing is that cache is local and not static, but maybe I'm wrong and...

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

给妤﹃绝世温柔 2024-10-29 23:19:54

这是 memoization 的简单 C++1x 实现。

memoize 函数返回一个闭包。返回值是一个函数,其状态不同于通过参数传递的状态(在本例中为 cache 变量)。

匿名函数中的 [=] 位指示返回的函数应获取所有局部变量的副本。 cache 变量不是静态的,因为它应该在返回函数的调用之间共享。

因此,每次调用 memoize 都会返回一个不同的函数及其自己的缓存。对 memoize 返回的特定闭包的后续调用将从该闭包的缓存中插入/获取值。

您可以将其视为在某种程度上等同于更老式的 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];
    }
};

This is simple C++1x implementation of memoization.

The memoize function returns a closure. The return value is a function that has state other than what is passed through the arguments (in this case, the cache variable).

The [=] bit in the anonymous function indicates that the returned function should take a copy of all local variables. The cache variable is not static because it is meant to be shared across invocations of the returned function.

Thus, each call to memoize will return a different function with it's own cache. Subsequent calls to a specific closure returned by memoize will insert/fetch values from that closure's cache.

You can think of this as a somewhat equivalent to the more old-school OOP version:

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];
    }
};
新人笑 2024-10-29 23:19:54

缓存嵌入到 lambda 本身中,并且位于其本地。

因此,如果您创建两个 lambda,每个都将拥有自己的缓存。

这是实现简单缓存的好方法,因为这样一旦 lambda 超出范围,所使用的内存就会被清除,并且不会出现内存爆炸的情况。

The cache is embedded into the lambda itself, and local to it.

Therefore, if you create two lambdas, each will have a cache of its own.

It's a great way to implement a simple cache, since this way the memory used is purged as soon as the lambda goes out of scope, and you don't have an explosion of memory.

不交电费瞎发啥光 2024-10-29 23:19:54

只要正确调用,“这段简单的代码”也可以记忆递归函数。这里我举一个完整的例子:

#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;
}

"This simple piece of code" can memoize recursive functions too, provided it is properly invoked. Here I give a complete example:

#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;
}
暖心男生 2024-10-29 23:19:54

引用您找到此内容的博客,就在代码下方:

...[=] 中的等号表示“捕获
周围的局部变量
按价值范围”,这是需要的
因为我们要返回 lambda
函数,局部变量将
那一刻就消失了。

因此,cache 被复制到返回的函数对象中,就像它是一个成员一样。

(请注意,这段简单的代码将无法记忆递归函数。实现定点C++0x 中的组合器 留给读者作为练习。)

To quote from the blog where you found this, just below the code:

... the equals sign in [=] means “capture
local variables in the surrounding
scope by value”, which is needed
because we are returning the lambda
function, and the local variable will
disappear at that moment.

So, cache is copied into the returned function object as if it were a member.

(Note that this simple piece of code will fail to memoize a recursive function. Implementing a fixed-point combinator in C++0x is left as an exercise to the reader.)

韵柒 2024-10-29 23:19:54

欢迎来到词法作用域的奇妙世界。它可用于创建具有公共和私有成员的整个类型。在函数式语言中,这通常是做到这一点的唯一方法。

我建议您阅读 http://mark-story .com/posts/view/picking-up-javascript-closures-and-lexical-scoping,这是关于 Javascript 的,但 C++0x 向 C++ 添加了相同的概念和(几乎相同)的行为。

Welcome to the wonderful world of lexical scoping. It can be used to create entire types with public and private members. In functional languages, it's often the only way to do that.

I suggest you read http://mark-story.com/posts/view/picking-up-javascript-closures-and-lexical-scoping, which is about Javascript, but C++0x adds the same concepts and (almost the same) behavior to C++.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文