(更新!)
C ++でのLINQのようなライブラリの多くの実装の中には、多くの興味深い、便利で効果的なものがあります。 しかし、私の意見では、それらのほとんどは、言語としてのC ++を無視して書かれています。 これらのライブラリのすべてのコードは、あのさを修正しようとしているように書かれています。 私はC ++が大好きだと認めています。 そして、それがどのように泥に注がれようとも、彼への私の愛は消え去りそうにない。 おそらくこれは、それが私の最初の高度なプログラミング言語であり、アセンブラーの後に学んだのは2番目のプログラミング言語だからかもしれません。
重要な更新 :Relinxの大きな変更! relinx_objectはstd :: enable_shared_from_thisからのmixinになり、std :: shared_ptrとして使用されます。 この変更により、relinx_objectをヒープメモリに配置し、変換チェーン全体のライフサイクルを制御できます。 現在、std :: shared_ptr <relinx_object>は、コンテナーに具体化することなく、関数およびストリームに渡すことができます。 ユーザーコードの唯一の変更点は、ドットではなく->を介したオブジェクトへのアクセスの置き換えです。たとえば、before from({1、2、3}) 。 count()、from from({1、2、3}) -> count()。 最後に、Relinxのコードはnstdと呼ばれる他のプロジェクトに転送されました 。これはこちらにあります 。
なんで?
これは永遠の、そして当然の問題です。 「なぜ、LINQのようなライブラリがたくさんあるのに、利用してみますか?」 一部には、そのようなライブラリの実装に関する私自身のビジョンのために、それを書きました。 一部には、LINQメソッドを可能な限り最大限に実装するライブラリを使用する必要があるため、必要に応じて、ある言語から別の言語に最小限の変更でコードを転送できます。
私の実装の特徴:
- C ++ 14標準(特に、多相ラムダ式)を使用する
- 順次アクセスでのみアダプターイテレーターを使用する(フォワード専用/入力イテレーター)。 これにより、std :: forward_listなど、さまざまな理由でランダムアクセスができないあらゆるタイプのコンテナおよびオブジェクトを使用できます。 これにより、std :: begin、std :: endをサポートするカスタムコレクションオブジェクトの開発も少し簡単になり、イテレータ自体は演算子*、operator!= And演算子++のみをサポートする必要があります。 したがって、ところで、カスタム型の新しいforステートメントは機能します。
- Relinxオブジェクトは、別のタイプのコンテナーに変換せずに新しいforステートメントで反復するのに適しています。また、ネイティブコンテナーの反復子のタイプに応じて、他のSTL関数アルゴリズムで反復するのにも適しています。
- このライブラリは、LINQメソッドのほとんどすべてのバリアントを何らかの形で実装しています。
- Relinxオブジェクトは、ネイティブコレクション上で可能な限り薄くなっています。
- ライブラリは、パラメータ転送を使用し、必要に応じてコピーではなく移動セマンティクスを実装します。
- ライブラリは、コレクションの要素(たとえば、last、element_at、reverse)へのランダムアクセスを必要とする操作を除き、十分に高速です。
- ライブラリは簡単に拡張可能です。
- ライブラリはMITのもとでライセンスされています。
一部のC ++プログラマーは、イテレーターが気に入らないため、イテレーターを何らかの方法で置き換えようとします(例えば、範囲に置き換えたり、イテレーターをまったく使用しません)。 ただし、新しいC ++ 11標準では、カスタムコレクションオブジェクトのforステートメントをサポートするために、forステートメントに反復子(またはポインターなどの反復可能な型)を提供する必要があります。 そして、この要件は単なるSTLではなく、言語そのものです。
LINQメソッドとRelinxメソッドの対応表:
LINQメソッド | Relinxメソッド |
---|---|
集計 | 集約する |
全部 | すべて |
なし | |
どれでも | どれでも |
想定される | から |
アバラジ | アバレッジ |
キャスト | キャスト |
連結 | 連結 |
コンテナ | 含む |
カウント | 数える |
サイクル | |
DefaultIfEmpty | デフォルト_ if _ empty |
明確な | 明確な |
エレメンタ | element_at |
ElementAtOrDefault | element_at_or_default |
空の | から |
を除く | を除く |
最初に | 最初に |
FirstOrDefault | first_or_default |
for_each 、 for_each_i | |
Groupby | group_by |
グループ参加 | group_join |
交差する | 交差する |
参加する | 参加する |
最後 | 最後の |
LastOrDefault | last_or_default |
ロングカウント | 数える |
マックス | 最大 |
分 | 分 |
タイプ | of_type |
オーダーバイ | order_by |
OrderByDescending | order_by_descending |
範囲 | 範囲 |
繰り返す | 繰り返す |
逆 | 逆に |
選択してください | select、select_i |
SelectMany | select_many 、 select_many_i |
シーケンス | sequence_equal |
シングル | 独身 |
SingleOrDefault | single_or_default |
スキップする | 飛ばす |
スキップする | skip_while、skip_while_i |
合計 | 合計 |
取る | 取る |
TakeWhile | take_while、take_while_i |
それでは | then_by |
ThenByDescending | then_by_descending |
トーアレイ | to_container、to_vector |
辞典 | to_map |
トーリスト | to_list |
ルックアップ | to_multimap |
to_string | |
連合 | union_with |
どこで | where、where_i |
郵便番号 | zip |
どうやって?
ライブラリのソースコードは、メソッドの使用例とともにDoxygenブロックで文書化されています。 また、メソッド実行の結果を制御してC#の結果に一致させるための、単純な単体テストがあります。ほとんどは私が作成しました。 しかし、それら自体は、ライブラリを使用する簡単な例として役立ちます。 作成とテストには、MinGW / GCC 5.3.0、Clang 3.9.0、およびMSVC ++ 2015コンパイラーを使用しましたが、C MSVC ++ 2015には単体テストのコンパイルに問題があります。 私が理解できた限り、このコンパイラは複雑なラムダ式を誤解しています。 たとえば、ラムダ内でfromメソッドを使用すると、奇妙なコンパイルエラーがクラッシュすることに気付きました。 リストされている他のコンパイラにはこのような問題はありません。
ライブラリはヘッダーファイルのみであり、使用するモジュールに含める必要があります。
使用する前に、便宜上、relinx名前空間を挿入することもできます。
使用例:
簡単な使用。 奇数の数を計算するだけです:
auto result = from({1, 2, 3, 4, 5, 6, 7, 8, 9})->count([](auto &&v) { return !!(v % 2); }); std::cout << result << std::endl; // : 5
例はより複雑です-グループ化:
struct Customer { uint32_t Id; std::string FirstName; std::string LastName; uint32_t Age; bool operator== (const Customer &other) const { return Id == other.Id && FirstName == other.FirstName && LastName == other.LastName && Age == other.Age; } }; //auto group_by(KeyFunction &&keyFunction) const noexcept -> decltype(auto) std::vector<Customer> t1_data = { Customer{0, "John"s, "Doe"s, 25}, Customer{1, "Sam"s, "Doe"s, 35}, Customer{2, "John"s, "Doe"s, 25}, Customer{3, "Alex"s, "Poo"s, 23}, Customer{4, "Sam"s, "Doe"s, 45}, Customer{5, "Anna"s, "Poo"s, 23} }; auto t1_res = from(t1_data)->group_by([](auto &&i) { return i.LastName; }); auto t2_res = from(t1_data)->group_by([](auto &&i) { return std::hash<std::string>()(i.LastName) ^ (std::hash<std::string>()(i.FirstName) << 1); }); assert(t1_res->count() == 2); assert(t1_res->first([](auto &&i){ return i.first == "Doe"s; }).second.size() == 4); assert(t1_res->first([](auto &&i){ return i.first == "Poo"s; }).second.size() == 2); assert(from(t1_res->first([](auto &&i){ return i.first == "Doe"s; }).second)->contains([](auto &&i) { return i.FirstName == "Sam"s; })); assert(from(t1_res->first([](auto &&i){ return i.first == "Poo"s; }).second)->contains([](auto &&i) { return i.FirstName == "Anna"s; })); assert(t2_res->single([](auto &&i){ return i.first == (std::hash<std::string>()("Doe"s) ^ (std::hash<std::string>()("John"s) << 1)); }).second.size() == 2); assert(t2_res->single([](auto &&i){ return i.first == (std::hash<std::string>()("Doe"s) ^ (std::hash<std::string>()("Sam"s) << 1)); }).second.size() == 2);
グループ化の結果は、std ::ペアのシーケンスです。最初はキーで、2番目はstd ::ベクトルコンテナー内のこのキーでグループ化されたCustomer要素です。 この例では、同じクラスの複数のフィールドによるグループ化はハッシュキーによって行われますが、これは必須ではありません。
そして、ここにgroup_joinの使用例があります。これは、ちなみに、ラムダ式自体のネストされたrelinx要求のために、MSVC ++ 2015でのみコンパイルされません。
struct Customer { uint32_t Id; std::string FirstName; std::string LastName; uint32_t Age; bool operator== (const Customer &other) const { return Id == other.Id && FirstName == other.FirstName && LastName == other.LastName && Age == other.Age; } }; struct Pet { uint32_t OwnerId; std::string NickName; bool operator== (const Pet &other) const { return OwnerId == other.OwnerId && NickName == other.NickName; } }; //auto group_join(Container &&container, ThisKeyFunction &&thisKeyFunction, OtherKeyFunction &&otherKeyFunction, ResultFunction &&resultFunction, bool leftJoin = false) const noexcept -> decltype(auto) std::vector<Customer> t1_data = { Customer{0, "John"s, "Doe"s, 25}, Customer{1, "Sam"s, "Doe"s, 35}, Customer{2, "John"s, "Doe"s, 25}, Customer{3, "Alex"s, "Poo"s, 23}, Customer{4, "Sam"s, "Doe"s, 45}, Customer{5, "Anna"s, "Poo"s, 23} }; std::vector<Pet> t2_data = { Pet{0, "Spotty"s}, Pet{3, "Bubble"s}, Pet{0, "Kitty"s}, Pet{3, "Bob"s}, Pet{1, "Sparky"s}, Pet{3, "Fluffy"s} }; auto t1_res = from(t1_data)->group_join(t2_data, [](auto &&i) { return i.Id; }, [](auto &&i) { return i.OwnerId; }, [](auto &&key, auto &&values) { return std::make_pair(key.FirstName + " "s + key.LastName, from(values). select([](auto &&i){ return i.NickName; }). order_by(). to_string(",")); } )->order_by([](auto &&p) { return p.first; })->to_vector(); assert(t1_res.size() == 3); assert(t1_res[0].first == "Alex Poo"s && t1_res[0].second == "Bob,Bubble,Fluffy"s); assert(t1_res[1].first == "John Doe"s && t1_res[1].second == "Kitty,Spotty"s); assert(t1_res[2].first == "Sam Doe"s && t1_res[2].second == "Sparky"s); auto t2_res = from(t1_data)->group_join(t2_data, [](auto &&i) { return i.Id; }, [](auto &&i) { return i.OwnerId; }, [](auto &&key, auto &&values) { return std::make_pair(key.FirstName + " "s + key.LastName, from(values). select([](auto &&i){ return i.NickName; }). order_by(). to_string(",")); } , true)->order_by([](auto &&p) { return p.first; })->to_vector(); assert(t2_res.size() == 6); assert(t2_res[1].second == std::string() && t2_res[3].second == std::string() && t2_res[5].second == std::string());
この例では、最初の操作の結果は、内部結合メソッドを使用したキーによる2つの異なるオブジェクトの結合であり、それらによってオブジェクトをグループ化します。
2番目の操作では、左結合方式を使用してキー結合が実行されます。 これは、trueに設定されたメソッドの最後のパラメーターによって示されます。
そして、これは多相型のフィルタリングの使用例です:
//auto of_type() const noexcept -> decltype(auto) struct base { virtual ~base(){} }; struct derived : public base { virtual ~derived(){} }; struct derived2 : public base { virtual ~derived2(){} }; std::list<base*> t1_data = {new derived(), new derived2(), new derived(), new derived(), new derived2()}; auto t1_res = from(t1_data)->of_type<derived2*>(); assert(t1_res->all([](auto &&i){ return typeid(i) == typeid(derived2*); })); assert(t1_res->count() == 2); for(auto &&i : t1_data){ delete i; };
コードは次の場所にあります。
GitHub: https : //github.com/Ptomaine/nstd、https : //github.com/Ptomaine/Relinx
ライブラリの使用に関する質問に答える準備ができており、機能性と気づいたエラーを改善するための建設的な提案に非常に感謝しています。