LINQ for PHP。 パート2.山がモハメッドに行かない場合、モハメッドは山に行きます

PHPのLINQライブラリを比較した前回の記事からわかるように、多くのライブラリがあり、品質が低くなっています。遅延計算はどのライブラリにも実装されていません。 。 それで私は自分のライブラリを書きました。 会う:



YaLinqo-PHPのオブジェクトへのもう1つのLINQ

機能:





コード例:



//     ,    , //   .      ,   . from($categories) ->orderBy('$v["name"]') ->groupJoin( from($products) ->where('$v["quantity"] > 0') ->orderByDescending('$v["quantity"]') ->thenBy('$v["name"]'), '$v["id"]', '$v["catId"]', 'array("name" => $v["name"], "products" => $e)' );
      
      





実装されたメソッド



次に、上記の例をさらに詳しく検討します。 実際、このクエリを記述するためのいくつかのオプションがあります。クロージャの使用と文字列ラムダの使用です。 ラムダには2つの構文もあります。デフォルトの変数名(それぞれ値とキーにvとk)を使用でき、意味のある変数名を設定できます。



初期データ(データベース、またはJSONサービス、「鉄」定数、またはその他のソースから):



 $products = array( array('name' => 'Keyboard', 'catId' => 'hw', 'quantity' => 10, 'id' => 1), array('name' => 'Mouse', 'catId' => 'hw', 'quantity' => 20, 'id' => 2), array('name' => 'Monitor', 'catId' => 'hw', 'quantity' => 0, 'id' => 3), array('name' => 'Joystick', 'catId' => 'hw', 'quantity' => 15, 'id' => 4), array('name' => 'CPU', 'catId' => 'hw', 'quantity' => 15, 'id' => 5), array('name' => 'Motherboard', 'catId' => 'hw', 'quantity' => 11, 'id' => 6), array('name' => 'Windows', 'catId' => 'os', 'quantity' => 666, 'id' => 7), array('name' => 'Linux', 'catId' => 'os', 'quantity' => 666, 'id' => 8), array('name' => 'Mac', 'catId' => 'os', 'quantity' => 666, 'id' => 9), ); $categories = array( array('name' => 'Hardware', 'id' => 'hw'), array('name' => 'Operating systems', 'id' => 'os'), );
      
      





実際には、タスク:ゼロ以外の数量で製品をフィルタリングし、適切なカテゴリに入れます。 最初に製品を数量の降順で並べ替え、次に名前で並べ替えます。 名前別に分類されたカテゴリ。 以下を取得する必要があります(簡潔にするために再フォーマットされています)。



 Array ( [hw] => Array ( [name] => Hardware [products] => Array ( [0] => Array ( [name] => Mouse [catId] => hw [quantity] => 20 [id] => 2 ) [1] => Array ( [name] => CPU [catId] => hw [quantity] => 15 [id] => 5 ) [2] => Array ( [name] => Joystick [catId] => hw [quantity] => 15 [id] => 4 ) [3] => Array ( [name] => Motherboard [catId] => hw [quantity] => 11 [id] => 6 ) [4] => Array ( [name] => Keyboard [catId] => hw [quantity] => 10 [id] => 1 ) ) ) [os] => Array ( [name] => Operating systems [products] => Array ( [0] => Array ( [name] => Linux [catId] => os [quantity] => 666 [id] => 8 ) [1] => Array ( [name] => Mac [catId] => os [quantity] => 666 [id] => 9 ) [2] => Array ( [name] => Windows [catId] => os [quantity] => 666 [id] => 7 ) ) ) )
      
      





以下は、PHP 5.3のクロージャーを使用した例です。 最長の記録ですが、さまざまなIDEで最高のサポートを提供しています。



 from($categories) ->orderBy(function ($cat) { return $cat['name']; }) ->groupJoin( from($products) ->where(function ($prod) { return $prod["quantity"] > 0; }) ->orderByDescending(function ($prod) { return $prod["quantity"]; }) ->thenBy(function ($prod) { return $prod["name"]; }), function ($cat) { return $cat["id"]; }, function ($prod) { return $prod["catId"]; }, function ($cat, $prods) { return array("name" => $cat["name"], "products" => $prods); } );
      
      





文字列ラムダを使用した記録。 演算子「==>」の左側に引数名があり、右側に戻り値があります。



 from($categories) ->orderBy('$cat ==> $cat["name"]') ->groupJoin( from($products) ->where('$prod ==> $prod["quantity"] > 0') ->orderByDescending('$prod ==> $prod["quantity"]') ->thenBy('$prod ==> $prod["name"]'), '$cat ==> $cat["id"]', '$prod ==> $prod["catId"]', '($cat, $prods) ==> array("name" => $cat["name"], "products" => $prods)' );
      
      





そして最後に、最短のエントリー。 演算子「==>」がない場合は、デフォルトの変数名が使用されます。vは値、kはキー、aとbは比較された値などです。



 from($categories) ->orderBy('$v["name"]') ->groupJoin( from($products) ->where('$v["quantity"] > 0') ->orderByDescending('$v["quantity"]') ->thenBy('$v["name"]'), '$v["id"]', '$v["catId"]', 'array("name" => $v["name"], "products" => $e)' );
      
      





(疑わしい)アーキテクチャソリューション

元のLINQを1対1でコピーすることはできません。異なる言語、異なる機能、異なる機能です。 したがって、多くの場合、選択する必要がありました。 あなたが判断するのはどれほど良いか悪いかです。 議論は大歓迎です。



キー

最も疑わしいものから始めましょう。キーは重要なデータとして宣言されます。 理由:ネイティブpokepashnyhイテレータに明確に存在し、配列で重要であり、JSONに変換するときに重要です。 一般に、キーはPHPのあらゆる場所で使用されるため、他のライブラリのようにキーを無視したくありません。



ただし、元のLINQシーケンスにはキーがないため、コールバック(可能な場合はすべてキーで機能できるようになりました)とLINQメソッド自体の両方の引数の数を増やす必要があります:resultSelectorはresultSelectorValue + resultSelectorKeyに変わります。 ただし、ほとんどの場合、開発者はこれについて考える必要はありません。コールバックはより少ない引数で渡すことができ、すべてのLINQメソッドにはresultSelectorKeyタイプの引数のデフォルト値があります。



キーに関する別の問題は、キーをどこにでも保存する必要があることに起因します。 つまり、デフォルトでは、要素をソートするときに同じキーが使用されます。 PHPは通常、要素を追加した順に配列をリストします。したがって、配列への変換は問題になりませんが、あなたは決して知りません。



重要な情報が必要ない場合は、それらを削除する2つの簡単な方法があります。



引数の順序

2番目は、関数とコールバックの引数の順序に関する疑わしい決定です。 それらは常に(理論的に)最も使用されているものから最も使用されていないものの順になります。 そのため、コールバックでは、通常、値はほとんどの場合重要ですが、キーは重要ではないため、通常は最初に値、次にキーが来ます。 ただし、引数の順序を覚えるのが難しくなる場合があります。 たとえば、selectでは値が最初に選択され、toDictionaryではキーが選択されます。



しかし、pokhapeshnikiはそのような不名誉に慣れることができません-言語全体が引数の完全にランダムな順序(同じ針と干し草の山)でまだらにされています。



アイテムインデックス

元のLINQを使用したユーザー向けの明らかなソリューション:indexOf、elementAtなどのメソッドは、列挙内の要素の数値位置ではなく、キーを使用します。 ポジションが必要な場合は、最初にtoValuesを呼び出します-キーはシーケンシャルになります:0、1、2、3など。 また、タイプselectのメソッドの場合、要素の位置を受け入れるコールバックによるオーバーロードはありません。 同様に、toValuesを使用します。



Lambd引数

私が書くことに触発されたlinq.jsライブラリでは、すべてのコールバックに$、$$、$$$、$$$という引数があります。 これはPHPでは発生しません。 文字列変換を行うことはできますが、コードが行内にある場合でも、有効なコードのままにしておきます。 引数を空の$ a、$ b、$ cとも呼びたくありません。 したがって、コンテンツに対応する名前を使用することが決定されました。





欠点:名前を知る必要があります。 ただし、詳細なドキュメントがあれば、これは問題になりません。



「疑わしい」コレクション

Listクラスはありません。toListメソッドは、シーケンシャルキー(0、1、2など)のみを使用してtoArrayと同じを返します。



辞書クラスです。 もともとはLookupのベースとしてのみ考えられていましたが、現在は別の完全なコレクションになっています。 通常の配列とは異なり、オブジェクトはキーである可能性があります(おそらく元のLINQで)。 しかし実際、PHPではforeachでキーオブジェクトを使用できないため、LINQ自体はどこでもキーオブジェクトをサポートしていません。 あなたはすべてのサイクルを書き直すことができますが、ゲームがどれほどキャンドルの価値があるかは問題です。



Lookupクラスは。 キーごとに値のリストを返します(キーがない場合は空の配列)。



どちらのコレクションも、内部配列を返すtoArrayメソッドをサポートしています。



MSDNのドキュメント

MSDNのヘルプは、すべてのメソッドにコピーされ、ポートの現実に適応されました。 どこかの説明は他のプロジェクトからのものです。 どこか-独立して書かれた。 エラーを見つけた場合は報告してください。



一般に、証明書は非常に堅実でした。 いくつかの方法は、そのような記事に病弱ではありません。



メソッド名

PHPの一部の単語は、言語自体とすべてのレジスターで素晴らしくつかまれています。 空でもメソッド名として使用できません。 したがって、競合がある場合、メソッドの名前が変更されます(記事の冒頭のメソッドのリストでは、元のメソッド名が括弧内に示されています)。 特に、run / forEachはcall / eachになりました。



例外名

.NETにあるPHPには組み込みの例外はありません。 ただし、不必要なクラスを作成しないようにしました。 そのため、InvalidOperationExceptionの代わりに、UnexpectedValueExceptionが使用されます。 最終的に、操作は予期しない値で無効になります。



安定したソート

ソートは不安定です。 つまり、ネストされた配列の最初の要素に従って配列[[0,1], [1,0], [0,2]]



ソートする場合、 [0,1]



および[0,2]



が後に続くことを保証する人はいませんその順序で。 結果は[[0,1], [0,2], [1,0]]



[[0,2], [0,1], [1,0]]



両方になります。



なんで? PHPには安定した並べ替えのための関数がなく、usortはライブラリ内で使用されるためです。 理論的には、元のLINQのようにソートを安定させることができますが、必要ですか? 誰もが、実行時間とメモリ使用量の安定性にお金を払わなければなりません。 「PHPパス」に従っているので、不安定性はPHP自体と同じである必要があると判断しました。



その他

単体テストのカバレッジはほぼ100%です。



ライセンスは簡易BSD(2ポイント)です。



要件-PHP 5.3。



使用法:



 require_once __DIR__ . '/lib/Linq.php'; //     use \YaLinqo\Enumerable; //     use \YaLinqo\Enumerable as E; //   //      from,     Enumerable —   Enumerable::from(array(1, 2, 3)); from(array(1, 2, 3));
      
      





他のライブラリとの比較

わかりやすくするために、JavaScript用のライブラリもテーブルに追加しました。 彼らの比較は別の記事になります。







ウィキペディアのような伝説ですが、追加の意味があります:





表の英語をおaびします。 ロシア語では長すぎた。



ありがとう

彼は無料で働いており、誰もお金をあげません。 寛大な攻撃を感じた場合は、PHPおよびPHPStormでこれらの機能に投票するだけです。 おそらく、彼らはライブラリに気づき、使用することがより快適になるでしょう。



Php

  1. イテレータ::キー()は、数字と文字列のみを返すことができます。
    1. 45684 foreachがキータイプに依存しないという要求
  2. クロージャーの構文を短縮する機能があり、パッチ、分析などが添付されました-機能を完成させた開発者が最善を尽くしました。 しかし、この機能は「nafig need」という結果で閉じられました。 :-(


PHPStorm IDE

  1. 行内のPHPコード
    • WI-3477 assert( 'literal')、evalなどにPHP言語を挿入
    • WI-2377挿入された言語で文字列内のphp変数のオートコンプリートがありません
  2. PHPコード分析
    • WI-11110未定義のメソッド:クロージャーを使用すると、未定義のメソッドが誤って報告されます
  3. PHPDocコメント
    • {link}が2回続けて使用された場合、PhpDocクイックドキュメントのWI-8270エラー


リンク

GitHubからPHP用オブジェクトへのもう1つのLINQをダウンロードする



PS同様の記事を英語で投稿できる場所を教えてください。



All Articles