在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; }