【问题标题】:Higher-order functions with templates?带有模板的高阶函数?
【发布时间】:2014-10-23 15:37:04
【问题描述】:

我已经用 Clojure(一种函数式语言)编程有一段时间了,我必须使用 C++ 来上课。我一直在尝试使用我在 Clojure 中喜欢的一些功能(例如,高阶函数、lambda、参数线程、动态类型等),但我遇到了一些难题。

首先,我实现了一个函数get,它接受两个参数:

  1. 一个集合(向量、列表、哈希映射等),以及
  2. 索引

并返回该索引处的元素。

我还实现了一个函数conj,它接受两个参数:

  1. 集合(向量、列表、队列等),以及
  2. 一个元素/对象(无论集合是什么类型)

并返回添加了元素的集合。在向量的情况下,这与push_back 基本相同。

现在,我希望能够使用如下高阶函数“转发”或“线程化”参数:

using std::vector;
vector<double> my_vec;

forward(my_vec,    // take "my_vec"
        conj(0.1), // "push" the value of 0.1 to the back of "my_vec"
        get(0),    // retrieve the first value
        inc);      // increment that value

它与inc(get(conj(my_vec, 0.1), 0); 相同,但更具可读性(!)。

forward 在这种情况下的返回值应该是 1.1。

为了使forward 函数工作,初始参数之后的参数必须都是高阶函数。也就是说,它们需要类似于以下工作:

template<typename Func>
Func get(int i){
  return [i](vector<boost::any> coll)
           -> boost::optional<boost::any> {
             return get(coll, i);
           };
}

但是,编译器无法推断要返回的 lambda 函数的类型。此外,我的猜测是,基于我对boost::any 的极其有限的经验,它无法将vector&lt;double&gt; 转换为vector&lt;boost::any&gt;,尽管boost::any 明显声称它可以充当替代品几乎适用于任何类型。

我希望get 函数具有通用性,因此我不想使用boost::function&lt;double (vector &lt;double&gt;, int)&gt; 或任何类似的特定类型。

另外,如果从get 请求的索引超出范围,我将使用boost::optional 而不是vector 返回null_ptr

就目前而言,这是我的 forward 函数的外观:

template <typename T1>
optional<T1> forward (T1 expr1){
  return expr1;
}
template <typename T1, typename T2>
optional<T1> forward (T1 expr1, T2 expr2){
  return forward(expr2(expr1));
}
template <typename T1, typename T2, typename T3>
optional<T1> forward (T1 expr1, T2 expr2, T3 expr3){
  return forward(expr2(expr1), expr3);
}

等等。 ...

关于如何让这个forward 函数工作的任何想法?

我也很确定有一种比像我一样进行 arity-overloading 更有效的方法来实现它。

【问题讨论】:

标签: c++ functional-programming


【解决方案1】:

只是对 Horstling 答案的补充,实际上forward 可以更容易地实现:

template <typename Value>
Value forward(Value v) {
    return v;
}

template <typename Value, typename Func, typename... Funcs>
auto forward(Value v, Func f, Funcs... fs) -> decltype(forward(f(v), fs...)) {
    return forward(f(v), fs...);
}

C++14 让事情变得更加甜蜜:

template <typename Value>
Value forward(Value v) {
    return v;
}

template <typename Value, typename Func, typename... Funcs>
decltype(auto) forward(Value v, Func f, Funcs... fs) {
    return forward(f(v), fs...);
}

查看完整代码here(Coliru 似乎支持 boost)

【讨论】:

    【解决方案2】:

    这就是我能想到的:

    http://coliru.stacked-crooked.com/a/039905c5deff8dcf

    我没有使用 lambda,而是在三种不同的变体中使用了成熟的仿函数。它支持您的示例并且不需要类型擦除等(例如 boost::any 或 std::function)。

    boost::optional<double> result = forward(my_vec, conj(0.1), get(0), inc);
    

    另外,forward 函数被实现为可变参数模板,允许任意数量的函数。

    代码未加修饰,但或许能给一些启发。

    编辑:Anton 绝对正确,我的 forward 实现过于复杂。上面的链接现在指向他修改后的代码。

    【讨论】:

      猜你喜欢
      • 2020-08-30
      • 2020-01-02
      • 1970-01-01
      • 2019-04-20
      • 2021-04-09
      • 1970-01-01
      • 1970-01-01
      • 2013-10-26
      • 1970-01-01
      相关资源
      最近更新 更多