一种在C ++中实现惰性评估的方法 [英] A way of achieving lazy evaluation in C++

查看:71
本文介绍了一种在C ++中实现惰性评估的方法的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

所以我在回答一个关于懒惰评估的问题(在这里,我的回答是在这种情况下会显得过于杀伤力,但这个想法似乎很有趣),这让我思考了如何在C ++中完成惰性评估.我想出了一种方法,但是我不确定所有的陷阱.还有其他方法可以实现懒惰评估吗?怎么办呢?有什么陷阱以及这个设计和其他设计?

So I was answering a question about lazy evaluation (here, my answer is overkill for that case but the idea seems interesting) and it made me think about how lazy evaluation might be done in C++. I came up with a way but I wasn't sure of all the pitfalls in this. Are there other ways of achieving lazy evaluation? how might this be done? What are the pitfalls and this and other designs?

这是我的主意:

#include <iostream>
#include <functional>
#include <memory>
#include <string>

#define LAZY(E) lazy<decltype((E))>{[&](){ return E; }}

template<class T>
class lazy {
private:
    typedef std::function<std::shared_ptr<T>()> thunk_type;
    mutable std::shared_ptr<thunk_type> thunk_ptr;
public:
    lazy(const std::function<T()>& x)
        : thunk_ptr(
            std::make_shared<thunk_type>([x](){
                return std::make_shared<T>(x());
            })) {}
    const T& operator()() const {
        std::shared_ptr<T> val = (*thunk_ptr)();
        *thunk_ptr = [val](){ return val; };
        return *val;
    }
    T& operator()() {
        std::shared_ptr<T> val = (*thunk_ptr)();
        *thunk_ptr = [val](){ return val; };
        return *val;
    }
};

void log(const lazy<std::string>& msg) {
    std::cout << msg() << std::endl;
}

int main() {
    std::string hello = "hello";
    std::string world = "world";
    auto x = LAZY((std::cout << "I was evaluated!\n", hello + ", " + world + "!"));
    log(x);
    log(x);
    log(x);
    log(x);
    return 0;
}

我在设计中担心的一些事情.

Some things I was concerned about in my design.

  • decltype有一些奇怪的规则.我对decltype的使用是否有陷阱?我在LAZY宏中的E周围添加了额外的括号,以确保对单个名称进行公平对待,就像引用vec [10]一样.还有其他我不负责的事情吗?
  • 在我的示例中,有很多间接层.看来这是可以避免的.
  • 这是对结果的正确记忆吗,以便无论什么或多少东西引用惰性值,它都只会评估一次(我对此很有信心,但是惰性评估加上大量共享指针可能会抛出一个错误.循环)

您有什么想法?

推荐答案

  • 您可能希望拥有thunk_type并将其作为单独的对象进行引用.现在,lazy<T>的副本不会从原产地评估中获得任何收益.但是在那种情况下,您将获得其他间接访问.
  • 有时候您可能只需要使用模板就可以摆脱包装到std :: function中的情况.
  • 我不确定该值是否需要为shared_ptr.也许呼叫者应该决定.
  • 您将在每次访问中产生新的闭包.
    • You may want to have thunk_type and reference to it as a separate objects. Right now copy of lazy<T> will gain nothing from evaluation of origin. But in that case you'll get additional indirect access.
    • Sometimes you may get rid of wrapping into std::function by simply using templates.
    • I'm not sure that value needs to be shared_ptr. Maybe caller should decide that.
    • You are going to produce new closures on each access.
    • 考虑下一个修改:

      template<typename F>
      lazy(const F& x) :
        thunk_ptr([&x,&this](){
          T val = (*x)();
          thunk_ptr = [val]() { return val; };
          return val;
        })
      {}
      

      或者其他实现可能看起来像:

      Or alternative implementation might look like:

      template<typename F>
      auto memo(const F &x) -> std::function<const decltype(x()) &()> {
          typedef decltype(x()) return_type;
          typedef std::function<const return_type &()> thunk_type;
          auto thunk_ptr = std::make_shared<thunk_type>();
          auto *thunk_cptr = thunk_ptr.get();
      
          // note that this lambda is called only from scope which holds thunk_ptr
          *thunk_ptr = [thunk_cptr, &x]() {
              auto val = std::move(x());
              auto &thunk = *thunk_cptr;
              thunk = [val]() { return val; };
              // at this moment we can't refer to catched vars
              return thunk();
          };
      
          return [thunk_ptr]() { return (*thunk_ptr)(); };
      };
      

      这篇关于一种在C ++中实现惰性评估的方法的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆