【问题标题】:c++ How to evaluate a list of functions?c ++如何评估函数列表?
【发布时间】:2020-08-24 11:22:13
【问题描述】:

我正在创建一个允许用户评估函数列表的 c++ 库。例如,用户将提供三个功能

A mapper1(B);
B mapper2(C);
C mapper3(D);

我将根据输入 D 列表评估它们,检索 A 列表。

功能列表由用户提供。它在编译时是已知的,但我不知道。我该如何实现,例如,我应该使用什么数据结构来维护函数列表?

用户使用模板 API 提供功能:

template <typename T>
class Mapper {
public:
    unique_ptr<Mapper<N>> map(function<N(T)>);
}

【问题讨论】:

  • 只是为了确定,你想要这样的东西:Mapper m{ [](auto v) { return v * v; }, [](auto v) { return 2 * v; }, }; auto result = m(1,2,3); // result should be : 2, 4, 6 =&gt; 4, 16, 36
  • @ElvisOric 是的,这就是我想要的

标签: c++ lambda functional-programming


【解决方案1】:

你可以使用命令类型的std::list,在链接中定义命令类型的函数

http://www.vincehuston.org/dp/command.html

总结如下:

  1. 使用类似 execute() 的方法签名定义一个 Command 接口。

  2. 创建一个或多个派生类,封装以下部分子集:“接收器”对象、要调用的方法、要传递的参数。

  3. 为每个延迟执行请求实例化一个 Command 对象。

  4. 将 Command 对象从创建者(又名发送者)传递给调用者(又名接收者)。

  5. 调用者决定何时执行()。

附上示例:

class Giant {
public:
   Giant()       { m_id = s_next++; }
   void fee()    { cout << m_id << "-fee  "; }
   void phi()    { cout << m_id << "-phi  "; }
   void pheaux() { cout << m_id << "-pheaux  "; }
private:
   int  m_id;
   static int s_next;
};
int Giant::s_next = 0;

class Command {
public:
   typedef void (Giant::*Action)();
   Command( Giant* object, Action method ) {
      m_object = object;
      m_method = method;
   }
   void execute() {
      (m_object->*m_method)();
   }
private:
   Giant* m_object;
   Action m_method;
};

template <typename T>
class Queue {
public:
   Queue() { m_add = m_remove = 0; }
   void enque( T* c ) {
      m_array[m_add] = c;
      m_add = (m_add + 1) % SIZE;
   }
   T* deque() {
      int temp = m_remove;
      m_remove = (m_remove + 1) % SIZE;
      return m_array[temp];
   }
private:
   enum { SIZE = 8 };
   T*  m_array[SIZE];
   int m_add, m_remove;
};

int main( void ) {
   Queue que;
   Command* input[] = { new Command( new Giant, &Giant::fee ),
                        new Command( new Giant, &Giant::phi ),
                        new Command( new Giant, &Giant::pheaux ),
                        new Command( new Giant, &Giant::fee ),
                        new Command( new Giant, &Giant::phi ),
                        new Command( new Giant, &Giant::pheaux ) };

   for (int i=0; i < 6; i++)
      que.enque( input[i] );

   for (int i=0; i < 6; i++)
      que.deque()->execute();
   cout << '\n';
}

// 0-fee  1-phi  2-pheaux  3-fee  4-phi  5-pheaux

【讨论】:

  • 感谢您的回答!我不知道函数及其返回类型。它们是用户提供的 lambda。我也尝试过命令方法。由于我要评估大量对象,因此创建许多命令对象会带来开销。所以我正在寻找“直接”执行功能的方法。
  • 刚刚编辑了答案以包括未来的摘要
【解决方案2】:

这是一个概念证明,您可以根据需要更好地调整它。我将 mapper 定义为一个函数。

template <typename T>
std::vector<T> helper(std::vector<T> vec) {
  return vec;
}

template <typename T, typename Fun>
std::vector<T> helper(std::vector<T> vec, Fun fun) {
  std::vector<T> result;
  for (const auto& v : vec) {
    result.emplace_back(fun(v));
  }
  return result;
}

template <typename T, typename Fun, typename... Funs>
std::vector<T> helper(std::vector<T> vec, Fun fun, Funs... funs) {
  return helper(helper(vec, funs...), fun);
}

template <typename T, typename... Funs>
std::vector<T> mapper(std::vector<T> vec, Funs... funs) {
  return helper(vec, funs...);
}

int main() {
  auto result = mapper(
      std::vector<int>{1, 2, 3},
      [](auto v) { return v * v; },
      [](auto v) { return 2 * v; }, 
      [](auto v) { return v + 1; });

  for (const auto& v : result) {
    std::cout << v << std::endl;
  }
}

注意: 如果您需要不同的排序,请替换

return helper(helper(vec, funs...), fun);

与:

return helper(helper(vec, fun), funs...);

【讨论】:

    【解决方案3】:

    因为您的映射器具有不同的签名,您需要一个 tuple 来保存它们。而且我认为没有理由使用std::function,因为我们不需要它的类型擦除。

    我的想法很简单:我们将所有映射器存储在一个元组中,然后创建对它们的递归调用。 (请注意,为了简单起见,我不打扰转发。如果需要,您可以添加)。

    template <class... Args>
    struct Mapper
    {
        std::tuple<Args...> fs;
    
        Mapper(Args... args) : fs{args...} {}
    
        template <std::size_t I, class T>
        auto call(T arg)
        {
            if constexpr (I == sizeof...(Args))
                return arg;
            else
                return call<I + 1>(std::get<I>(fs)(arg));
        }
    
        template <class T>
        auto operator()(T arg)
        {
            return call<0>(arg);
        }
    };
    

    示例用法:

    auto test()
    {
        auto mapper = Mapper{mapper3, mapper2, mapper1};
    
        D d{};
        A a = mapper(d);
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-01-10
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多