コンテナと関数の両方に共通の抽象化があります。私はHaskellでそれを学んだので、C++で実装しようとしています。テンプレートを使用した関数型オーバーロード
ほとんどのC++プログラマはSTDに精通::変換、おおよそBへのタイプAから関数与え話している、あなたはあなたがで関数を変換することができ
タイプBのコンテナにタイプAのコンテナに変換することができます同様の方法で、AからBへの関数fooが与えられたとき、ZをAとする関数バーを関数fooに変換することができます。バーをZからBにします。実装は簡単です。単なる合成です。
汎用プログラミングの抽象概念を反映するために、関数fmapをコンテナと関数に定義したかったのです。
コンテナはしかし、機能のために類似した機能が、私は非常に神経質になり(私はこれは完全には一般的ではありません知っている)
template <typename A, typename Func>
auto fmap(Func f, vector<A> in) {
vector<decltype(f(in[0]))> out_terms{};
for(auto vec : in)
out_terms.push_back(f(vec));
return out_terms;
}
簡単でした。
template <typename FuncT, typename Func>
auto fmap(FuncT f, Func in) {
return [f, in](auto x){
return f(in(x));
};
}
テンプレートは呼び出し可能なもの以外のものを専門としませんが、これは過負荷の解決を混乱させることになります。名前空間をきれいに保つために、関数型への解決を制限するために、テンプレートパラメータに型制約を導入したいと思います。そして私はそれをどうやって行うのか尋ねるつもりでした。
この抽象化は非常に一般的です。値へのポインタに対応するfmapがありますが、これも競合する可能性があります。
私の質問は、同じテンプレートレベルの署名を持つ2つの異なるテンプレート実装を持つことができますか?私はほとんど答えがないと確信していますが、おそらく同様のものが偽造される可能性があります。そうでない場合、過負荷を区別するために現在どのようなツールが利用できますか?特に関数型の場合。
これは私にはわかりませんが、これは概念の教科書のようです。
編集:ブースト、特にSFINAEを使用しても問題ありません。私は、ほとんどのプログラマーにとって使い慣れた、便利で標準的なソリューションを見つけることを試みています。私はfmapの名前を変更することができましたが、プログラマーはfmapを受け入れるテンプレート関数に作成を渡すことを知る必要がありました。 fmapは意味的にユニークなので、残念です。
編集2:これをどのように使用するかの簡単な例。
template <typename T>
auto double_everything(T in){
auto doublef = [](auto x){return 2*x;};
return fmap(doublef, in);
}
それは、物事「のようなコンテナ」以上のマップへのコンテナの上にマップを一般化します。したがって、double_everything(vector<int> {1, 2, 3})
は、要素が2倍になったベクトルを返します。しかし、double_everything([](int x){ return x + 1; })
は、出力がインクリメント関数の出力の2倍の関数を返します。一種のリストを倍増させるようなものです。抽象化には素敵なプロパティがいくつかあります。ただそれを作り上げるだけではありません。とにかく、関数fmap
の名前を変更しても問題には答えません。
編集3:テンプレートC
ため fmap
はC<A>
からC<B>
を満たすfmap(compose(f, g) , c) = fmap(f, fmap(g, c))
に機能するA
からB
に機能を取ります。これは素敵な構造物のプロパティを保持しています。
範囲に対してこれを行う関数は、すでに別の名前で存在しています。しかし、rangeはタイプ上の唯一のテンプレートではありません。ここでstd::optional
ためfmap
です:
template<typename T, typename Func>
auto fmap(Func f, optional<T> o) -> optional<f(*o)>{
if(o)
return f(*o);
else
{};
}
この実装は、以前の提示機能のためのfmap
のように、全く任意の範囲の概念を必要としません。しかし、それはfmap
の意味的要件を満たす。
私は同じ方法で異なるオーバーロードのためにfmap
を定義しようとしていますが、私はカスタムマトリクスタイプのために新しいoperator *
を定義します。だから、私はboost::transform_iterator
の点でfmap
を喜んで定義します。次に、これらのアルゴリズムは、fmap
という用語で汎用的に機能します。ここで
は、このような機能の一例です:私たちは、印刷
auto lists = vector<vector<int> > { {1, 2, 3}, {4, 5, 6} };
auto lists_squared = map_one_deep([](int x){return x*x;} , lists);
lists_squared
を書いた場合、我々は代わりにoptionalsのベクトルを持っていた場合
template <
template<typename, typename> class Cont,
typename Fmappable,
typename Alloc,
typename Func>
auto map_one_deep(Func f, Cont<Fmappable, Alloc> c){
auto g = [f](Fmappable x){ return fmap(f, x); };
return fmap(g, c);
}
は今
1 4 9
16 25 36
を与え、選択肢は要素を含んでいれば二乗される。
私はC++の高階関数でどのように動作するべきかを理解しようとしています。
これは一般的なケースでは不可能です。私は関数 'compose'の名前を変更します。 – Barry
私はこの記事を評価するのに十分なC++をもう知りませんが、あなたがやろうとしていることには少なくとも関係していると思います:[C++のFunctorパターン](https://www.fpcomplete.com/blog/2012/ 07/the-functor-pattern-in-c) – chepner
SFINAEで関数のテンプレートタイプを制限することができます。 – Jarod42