C++結合関数
4173 ワード
結合関数は、N個の一元関数をより複雑な関数として構成し、各関数の戻り値は最後の関数に伝達されるまでパラメータとして次の関数に伝達される.この組合せ関数の能力は,複雑なチェーン実行挙動をより直感的に達成することができる.たとえば、次の3つの関数があります.
この方式は使いやすくなく、これらの簡単な関数を組み合わせることができれば、簡単な関数で呼び出すことができ、より直感的で簡潔になります.たとえば、次のように呼び出されます.
この方式はこれらの関数を直列に接続し,内部は次々と呼び出され,最終結果が得られる.
c++でこの組合せ関数の呼び出しをどのように実現しますか?どうすればいいか考えてみましょう.まず、この組合せ関数の呼び出しの特徴を分析します.
すべて一元関数です.戻り値は次の関数のパラメータになるため、戻り値は1つの結果しかありません.
一元関数のパラメータと戻り値は同じタイプです.戻り値は次の関数のパラメータになるからです.
行き先から順番に呼び出す.
上記の解析から,この組合せ関数には,戻り値とパラメータが同じでなければならないという暗黙の制約があり,これらの関数は一元関数にすぎないことが分かった.複数のパラメータが必要な場合は、data_と同様に構造体をパラメータとして使用できます.struct f(data_struct)は、複数のパラメータの問題を実現するために使用される.
では、c++でこの呼び出しがどのように実現されているかを見てみましょう.
テストコード:
composeについては以前のコードはgccでコンパイルされていたが、GCCとvs 2013が変参に対する展開ルールが異なるため、GCCの展開ルールがより厳しい.
完全なテストコード:
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++でこの組合せ関数の呼び出しをどのように実現しますか?どうすればいいか考えてみましょう.まず、この組合せ関数の呼び出しの特徴を分析します.
すべて一元関数です.戻り値は次の関数のパラメータになるため、戻り値は1つの結果しかありません.
一元関数のパラメータと戻り値は同じタイプです.戻り値は次の関数のパラメータになるからです.
行き先から順番に呼び出す.
上記の解析から,この組合せ関数には,戻り値とパラメータが同じでなければならないという暗黙の制約があり,これらの関数は一元関数にすぎないことが分かった.複数のパラメータが必要な場合は、data_と同様に構造体をパラメータとして使用できます.struct f(data_struct)は、複数のパラメータの問題を実現するために使用される.
では、c++でこの呼び出しがどのように実現されているかを見てみましょう.
template
class Composed
{
public:
explicit Composed(OuterFn outerFn, InnerFn innerFn) :m_outerFn(outerFn), m_innerFn(innerFn) {}
public:
template
auto operator()(Arg arg) -> decltype(declval()((declval()(declval()))))
{
return m_outerFn(m_innerFn(arg));
}
private:
InnerFn m_innerFn;
OuterFn m_outerFn;
};
template
Composed Compose(Function1 f1, Function2 f2)
{
return Composed(f1, f2);
}
template
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とvs 2013が変参に対する展開ルールが異なるため、GCCの展開ルールがより厳しい.
完全なテストコード:
#include
#include
using namespace std;
template
class Composed
{
public:
explicit Composed(OuterFn outerFn, InnerFn innerFn) :m_outerFn(outerFn), m_innerFn(innerFn) {}
public:
template
auto operator()(Arg arg) -> decltype(declval()((declval()(declval()))))
{
return m_outerFn(m_innerFn(arg));
}
private:
InnerFn m_innerFn;
OuterFn m_outerFn;
};
template
Composed Compose(Function1 f1, Function2 f2)
{
return Composed(f1, f2);
}
template
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;
}