1. 程式人生 > >C++ 組合函式

C++ 組合函式

組合函式是將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下編譯不過,原因是因為GCC和vs2013對於變參的展開規則不太一樣導致的,GCC的展開規則更嚴格。

完整測試程式碼:

#include <vector>
#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 main()
{
	TestCompose();
	system("pause");
	return 0;
}