简体   繁体   English

带模板的高阶函数?

[英]Higher-order functions with templates?

I've been programming in Clojure (a functional language) for a while now, and I have to use C++ for a class. 我已经用Clojure(一种函数式语言)编程了一段时间了,我必须在类中使用C ++。 I've been trying to use some of the functionality I've enjoyed in Clojure (eg, higher-order functions, lambdas, argument threading, dynamic typing, etc.) but I've been running into some brick walls. 我一直在尝试使用我在Clojure中享受的一些功能(例如,高阶函数,lambdas,参数线程,动态类型等)但我已经碰到了一些砖墙。

Firstly, I've implemented a function get which takes two arguments: 首先,我实现了一个带有两个参数的函数get

  1. a collection (vector, list, hash map, etc.), and 集合(向量,列表,哈希映射等),和
  2. an index 一个索引

and returns the element at that index. 并返回该索引处的元素。

I've also implemented a function conj which takes two arguments: 我还实现了一个函数conj ,它有两个参数:

  1. a collection (vector, list, queue, etc.), and 集合(矢量,列表,队列等),和
  2. an element/object (of whatever type the collection is) 元素/对象(集合的任何类型)

and returns the collection with the element added. 并返回添加了元素的集合。 In the case of vectors, this is largely the same as push_back . 在向量的情况下,这与push_back大致相同。

Now, I want to be able to "forward" or "thread" the arguments using higher-order functions like so: 现在,我希望能够使用高阶函数“转发”或“线程化”参数,如下所示:

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

It's the same as inc(get(conj(my_vec, 0.1), 0); , but a lot (!) more readable. 它与inc(get(conj(my_vec, 0.1), 0);但是很多(!)更具可读性。

The return value of forward in this case should be 1.1. 在这种情况下, forward的返回值应为1.1。

In order to get the forward function to work, the arguments after the initial argument need to all be higher-order functions. 为了使forward函数起作用,初始参数之后的参数需要都是高阶函数。 That is, they need to work analogous to the following: 也就是说,它们需要类似于以下工作:

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

However, the compiler can't deduce the type of the lambda function to be returned. 但是,编译器无法推断要返回的lambda函数的类型。 Also, my guess, based on my extremely limited experience with boost::any , is that it won't be able to convert a vector<double> to vector<boost::any> , despite the apparent claims of boost::any that it can act as a substitute for virtually any type. 此外,我的猜测,根据我极其有限的经验boost::any ,就是它不能给转换vector<double>vector<boost::any> ,尽管明显要求boost::any它可以作为几乎任何类型的替代品。

I want the get function to be general, so I don't want to use boost::function<double (vector <double>, int)> , or any similarly specific typing. 我希望get函数是通用的,所以我不想使用boost::function<double (vector <double>, int)>或任何类似的特定类型。

Also, I'm using boost::optional instead of vector to return null_ptr if the index requested from get is out of bounds. 另外,如果从get请求的索引超出范围,我使用boost::optional而不是vector来返回null_ptr

As it stands, here's how my forward function looks: 就目前而言,这是我的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);
}

etc. ... 等......

Any ideas on how to get this forward function to work? 关于如何使这个forward功能起作用的任何想法?

I'm also pretty sure there's a more efficient way to implement it than doing the arity-overloading as I have. 我也非常确定有一种更有效的方法来实现它,而不是像我一样进行arity-overloading。

That's what I can come up with: 这就是我能想到的:

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

Instead of lambdas I used fully fledged functors in three different variants. 我使用了三种不同变体的完全成熟的仿函数,而不是lambdas。 It supports your example and does not require type erase or such (eg boost::any or std::function). 它支持您的示例,不需要类型擦除等(例如boost :: any或std :: function)。

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

Also, the forward function is implemented as variadic template, allowing any number of functions. 此外, forward功能实现为可变参数模板,允许任意数量的功能。

The code is unpolished, but maybe it can give some inspiration. 代码是未经修改的,但也许它可以提供一些灵感。

EDIT: Anton is absolutely right, my implementation of forward was unnecessarily complicated. 编辑: 安东是绝对正确的,我对前锋的实施是不必要的复杂。 The link above now points to his revised version of the code. 上面的链接现在指向他修改后的代码版本。

Just an addition to Horstling's answer, actually forward can be implemented much easier: 只是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...);
}

And C++14 sweetens things even a little bit more: 而且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...);
}

Take a look at the full code here (Coliru appears to support boost) 看看这里的完整代码(Coliru似乎支持提升)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM