這次要講的是:C++11如何通過組合函數來簡化我們的程序。關於組合函數,大家可能對這個概念有點陌生。組合函數是將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...); }
寫好了,再測試一下:
int gt(int x) { return x; } int ht(int y) { return y; } 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); }
通過測試程序我們可以看到,我們可以組合任意多個一元函數,這些一元函數可以是function也可以是lamda,它們之間彼此獨立沒有關聯關系。這種組合是非常靈活的,也可以動態調整的。也許有人要問,這個東西有啥用啊,細想一下,它還是挺有價值的:
首先,它比原來的調用更加直觀和簡潔,其次它可以很方便的實現鏈式的函數調用,說到鏈式的函數調用,在做數據處理的時候比較有用,有可能需要對數據進行層層預處理,這些處理過程通過組合方式很容易實現,而且可以方便的增加或者減少處理函數,以及調換順序,這是非常靈活的。
其次,它可以很容易做成責任鏈模式,它比動態多態實現的責任鏈模式更加強大,這個鏈條可以動態調整,調用函數之間彼此可以沒有任何關系,沒有繼承這種強約束關系,使得我們可以靈活的實現責任鏈模式。我相信它的價值還有更多。
c++11 boost技術交流群:296561497,歡迎大家來交流技術。