在C ++中进行函数式编程。 实施f(a)(b)(c)
我一直在使用C ++进行函数式编程的基础知识。 我试图做一个函数f(a)(b)(c)
,将返回a + b + c
。 我成功实现了函数f(a)(b)
,它返回a + b。 这是它的代码:
std::function<double(double)> plus2(double a){ return[a](double b){return a + b; }; }
我只是不知道如何实现函数f(a)(b)(c)
,正如我以前所说,应该返回a + b + c
。
只要把你的2个元素的解决scheme,并扩大它,包装与另一个lambda。
既然你想返回一个lambdaexpression式来获得一个double
并返回一个double
s'addition lambda,那么你所需要做的就是用另一个函数包装你当前的返回types,并且将一个嵌套lambda添加到你当前的lambdaexpression式中(一个返回的lambdaexpression式拉姆达):
std::function<std::function<double(double)>(double)> plus3 (double a){ return [a] (double b) { return [a, b] (double c) { return a + b + c; }; }; }
-
正如@注意到的,你可以跳过
std::function<std::function<double(double)>(double)>
并与auto
:auto plus3 (double a){ return [a] (double b) { return [a, b] (double c) { return a + b + c; }; }; }
-
您可以使用更深的嵌套lambdaexpression式为每个元素扩展此结构。 示范4个要素:
auto plus4 (double a){ return [a] (double b) { return [a, b] (double c) { return [a, b, c] (double d) { return a + b + c + d; }; }; }; }
你可以通过让你的函数返回一个函子 ,即一个实现了operator()
的对象来实现。 这是一个办法:
struct sum { double val; sum(double a) : val(a) {} sum operator()(double a) { return val + a; } operator double() const { return val; } }; sum f(double a) { return a; }
例
链接
int main() { std::cout << f(1)(2)(3)(4) << std::endl; }
模板版本
你甚至可以编写一个模板化版本,让编译器推断出这个types。 在这里尝试。
template <class T> struct sum { T val; sum(T a) : val(a) {} template <class T2> auto operator()(T2 a) -> sum<decltype(val + a)> { return val + a; } operator T() const { return val; } }; template <class T> sum<T> f(T a) { return a; }
例
在这个例子中, T
最终将解决double
:
std::cout << f(1)(2.5)(3.1f)(4) << std::endl;
这是一个稍微不同的方法,它从operator()
返回*this
的引用,所以你没有任何副本浮动。 这是一个非常简单的函数实现,其recursion地存储状态和左侧的折叠:
#include <iostream> template<typename T> class Sum { T x_{}; public: Sum& operator()(T x) { x_ += x; return *this; } operator T() const { return x_; } }; int main() { Sum<int> s; std::cout << s(1)(2)(3); }
住在Coliru
这不是f(a)(b)(c)
,而是curry(f)(a)(b)(c)
。 我们包装f
,使得每个额外的参数要么返回另一个curry
要么真正地调用这个函数。 这是C ++ 17,但是可以在C ++ 11中实现一些额外的工作。
请注意,这是一个解决一个函数的问题 – 这是我从问题中得到的印象 – 而不是折叠二进制函数的解决scheme。
template <class F> auto curry(F f) { return [f](auto... args) -> decltype(auto) { if constexpr(std::is_invocable<F&, decltype(args)...>{}) { return std::invoke(f, args...); } else { return curry([=](auto... new_args) -> decltype(std::invoke(f, args..., new_args...)) { return std::invoke(f, args..., new_args...); }); } }; }
为了简洁,我跳过了参考。 示例用法是:
int add(int a, int b, int c) { return a+b+c; } curry(add)(1,2,2); // 5 curry(add)(1)(2)(2); // also 5 curry(add)(1, 2)(2); // still the 5th curry(add)()()(1,2,2); // FIVE auto f = curry(add)(1,2); f(2); // i plead the 5th
我能想到的最简单的方法是用plus2()
定义plus3()
plus2()
。
std::function<double(double)> plus2(double a){ return[a](double b){return a + b; }; } auto plus3(double a) { return [a](double b){ return plus2(a + b); }; }
这将前两个参数列表组合成一个单独的参数列表,用于调用plus2()
。 这样做使我们能够以最小的重复来重复使用我们原有的代码,并且可以在将来轻松地扩展; plusN()
只需要返回一个调用plusN-1()
的lambdaexpression式,这个lambdaexpression式plusN-1()
调用plusN-1()
传递给前一个函数,直到达到plus2()
。 它可以像这样使用:
int main() { std::cout << plus2(1)(2) << ' ' << plus3(1)(2)(3) << '\n'; } // Output: 3 6
考虑到我们只是简单地调用,我们可以很容易地把它变成一个函数模板,这样就不需要为额外的参数创build版本。
template<int N> auto plus(double a); template<int N> auto plus(double a) { return [a](double b){ return plus<N - 1>(a + b); }; } template<> auto plus<1>(double a) { return a; } int main() { std::cout << plus<2>(1)(2) << ' ' << plus<3>(1)(2)(3) << ' ' << plus<4>(1)(2)(3)(4) << ' ' << plus<5>(1)(2)(3)(4)(5) << '\n'; } // Output: 3 6 10 15
在这里看到两个行动。
我要去玩了。
你想要做一个加法的咖喱。 我们可以解决这个问题,或者我们可以解决包括这个问题在内的一类问题。
所以,第一,加法:
auto add = [](auto lhs, auto rhs){ return std::move(lhs)+std::move(rhs); };
这表示增加的概念很好。
现在,折叠:
template<class F, class T> struct folder_t { F f; T t; folder_t( F fin, T tin ): f(std::move(fin)), t(std::move(tin)) {} template<class Lhs, class Rhs> folder_t( F fin, Lhs&&lhs, Rhs&&rhs): f(std::move(fin)), t( f(std::forward<Lhs>(lhs), std::forward<Rhs>(rhs)) ) {} template<class U> folder_t<F, std::result_of_t<F&(T, U)>> operator()( U&& u )&&{ return {std::move(f), std::move(t), std::forward<U>(u)}; } template<class U> folder_t<F, std::result_of_t<F&(T const&, U)>> operator()( U&& u )const&{ return {f, t, std::forward<U>(u)}; } operator T()&&{ return std::move(t); } operator T() const&{ return t; } };
它需要一个种子值和一个T,然后允许链接。
template<class F, class T> folder_t<F, T> folder( F fin, T tin ) { return {std::move(fin), std::move(tin)}; }
现在我们连接它们。
auto adder = folder(add, 0); std::cout << adder(2)(3)(4) << "\n";
我们也可以使用folder
进行其他操作:
auto append = [](auto vec, auto element){ vec.push_back(std::move(element)); return vec; };
使用:
auto appender = folder(append, std::vector<int>{}); for (int x : appender(1)(2)(3).get()) std::cout << x << "\n";
现场示例 。
我们必须在这里调用.get()
,因为for(:)
循环不理解我们文件夹的operator T()
。 我们可以通过一些工作来解决这个问题,但是.get()
更容易。
如果你愿意使用库,这在Boost的Hana中是非常容易的 :
double plus4_impl(double a, double b, double c, double d) { return a + b + c + d; } constexpr auto plus4 = boost::hana::curry<4>(plus4_impl);
然后使用它就像你的愿望:
int main() { std::cout << plus4(1)(1.0)(3)(4.3f) << '\n'; std::cout << plus4(1, 1.0)(3)(4.3f) << '\n'; // you can also do up to 4 args at a time }
所有这些答案似乎非常复杂。
auto f = [] (double a) { return [=] (double b) { return [=] (double c) { return a + b + c; }; }; };
正是你想要的,而且它在C ++ 11中起作用,不像许多或者大多数其他的答案。
请注意,它不使用性能损失的std::function
,事实上,在很多情况下它可能会被内联。
下面是一个使用operator()
来改变状态的状态模式singleton的启发方法。
编辑:交换不必要的分配进行初始化。
#include<iostream> class adder{ private: adder(double a)val(a){} double val = 0.0; static adder* mInstance; public: adder operator()(double a){ val += a; return *this;} static adder add(double a){ if(mInstance) delete mInstance; mInstance = new adder(a); return *mInstance;} double get(){return val;} }; adder* adder::mInstance = 0; int main(){ adder a = adder::add(1.0)(2.0)(1.0); std::cout<<a.get()<<std::endl; std::cout<<adder::add(1.0)(2.0)(3.0).get()<<std::endl; return 0; }