组合函数
文章转自https://www.cnblogs.com/qicosmos/p/3292490.html
组合函数是将N个一元函数组成一种更复杂的函数,每个函数的返回值作为参数传给下一个函数,直到传到最后一个函数结束。这种组合函数的能力可以使我们以一种更直观的方式去完成复杂的链式执行行为。例如有三个函数:
int f(int x), int g(int y), int h(int z) 依次调用三个函数 int a,b,c,parm; a = f(parm); b = g(a); c = h(b); 等价于 c = h(g(f(parm)));
这种方式在使用起来不够简洁方便,如果能把这些简单函数组合起来,就可以按简单函数的方式去调用了,更加直观和简洁。比如像这样调用:
compose(f,g,h)(parm);
这种方式把这些函数串在一起了,内部是一个接一个调用并得到最终结果。
在c++中如何实现这种组合函数的调用呢?想想我们应该怎么做吧。我们先分析一下这种组合函数的调用的特点:
- 都是一元函数;因为返回值要做下个函数的入参,返回值只有一个结果。
- 一元函数的入参和返回值都是同一种类型;因为返回值要做下个函数的入参。
- 按顺序从前往后调用。
通过上面的分析我们知道这种组合函数有个隐含的约束就是,返回值和入参必须相同,这也导致这些函数只能是一元函数。 如果希望有多个入参,则要通过变通的方式了,比如可以将一个结构体作为入参,类似于data_struct f(data_struct)来实现多个入参的问题。
好了现在看看c++中是如何实现这种调用的吧。
template <typename OuterFn, typename InnerFn> class Composed { public: explicit Composed(OuterFn outerFn, InnerFn innerFn) :m_outerFn(outerFn), m_innerFn(innerFn) {} public: template <typename Arg> auto operator()(Arg arg) -> decltype(declval<OuterFn>()((declval<InnerFn>()(declval<Arg>())))) { return m_outerFn(m_innerFn(arg)); } private: InnerFn m_innerFn; OuterFn m_outerFn; }; template <typename Function1, typename Function2> Composed<Function1, Function2> Compose(Function1 f1, Function2 f2) { return Composed<Function1, Function2>(f1, f2); } template <typename Function1, typename Function2, typename Function3, typename... Functions> auto Compose(Function1 f1, Function2 f2, Function3 f3, Functions... fs)->decltype(Compose(Compose(f1, f2), f3, fs...)) { return Compose(Compose(f1, f2), f3, fs...); }
测试代码:
void TestCompose() { auto f1 = [](int a){return a + 1; }; auto g1 = [](int b){return b + 2; }; auto h1 = [](int c){return c + 3; }; auto I1 = [](int d){return d + 4; }; auto J1 = [](int e){return e + 5; }; auto ret = Compose(f1, g1, h1)(3); ret = Compose(f1, g1, h1, I1)(3); ret = Compose(f1, g1, h1, I1, J1)(3); ret = Compose(f1, g1, h1, I1, J1, J1, J1)(3); ret = Compose([](int d){return d + 4; }, [](int d){return d + 5; })(3); }
对于compose那里之前的代码在gcc下编译不过的问题做一个说明(Normandie反馈过这个问题,在此表示感谢),原因是因为GCC和vs2013对于变参的展开规则不太一样导致的,GCC的展开规则更严格。
完整代码:
#include "stdafx.h" #include "stdlib.h" #include <iostream> using namespace std; template <typename OuterFn, typename InnerFn> class Composed { public: explicit Composed(OuterFn outerFn, InnerFn innerFn) :m_outerFn(outerFn), m_innerFn(innerFn) {} public: template <typename Arg> auto operator()(Arg arg) -> decltype(declval<OuterFn>()((declval<InnerFn>()(declval<Arg>())))) { return m_outerFn(m_innerFn(arg)); } private: InnerFn m_innerFn; OuterFn m_outerFn; }; template <typename Function1, typename Function2> Composed<Function1, Function2> Compose(Function1 f1, Function2 f2) { return Composed<Function1, Function2>(f1, f2); } template <typename Function1, typename Function2, typename Function3, typename... Functions> auto Compose(Function1 f1, Function2 f2, Function3 f3, Functions... fs)->decltype(Compose(Compose(f1, f2), f3, fs...)) { return Compose(Compose(f1, f2), f3, fs...); } void TestCompose() { auto f1 = [](int a){return a + 1; }; auto g1 = [](int b){return b + 2; }; auto h1 = [](int c){return c + 3; }; auto I1 = [](int d){return d + 4; }; auto J1 = [](int e){return e + 5; }; auto ret = Compose(f1, g1, h1)(3); ret = Compose(f1, g1, h1, I1)(3); cout << ret << endl; ret = Compose(f1, g1, h1, I1, J1)(3); cout << ret << endl; ret = Compose(f1, g1, h1, I1, J1, J1, J1)(3); cout << ret << endl; ret = Compose([](int d){return d + 4; }, [](int d){return d + 5; })(3); cout << ret << endl; } int _tmain(int argc, _TCHAR* argv[]) { TestCompose(); system("pause"); return 0; }
通过测试程序我们可以看到,我们可以组合任意多个一元函数,这些一元函数可以是function也可以是lamda,它们之间彼此独立没有关联关系。这种组合是非常灵活的,也可以动态调整的。也许有人要问,这个东西有啥用啊,细想一下,它还是挺有价值的:
首先,它比原来的调用更加直观和简洁,其次它可以很方便的实现链式的函数调用,说到链式的函数调用,在做数据处理的时候比较有用,有可能需要对数据进行层层预处理,这些处理过程通过组合方式很容易实现,而且可以方便的增加或者减少处理函数,以及调换顺序,这是非常灵活的。
其次,它可以很容易做成责任链模式,它比动态多态实现的责任链模式更加强大,这个链条可以动态调整,调用函数之间彼此可以没有任何关系,没有继承这种强约束关系,使得我们可以灵活的实现责任链模式。我相信它的价值还有更多。