JavaScriptでゼロからLINQを書く

なぜ



かつて、Webブラウザー用のグラフコンポーネントの開発中に、JavaScriptでシーケンスを処理する問題に直面しました。



C#開発者は、長年にわたってシーケンス処理に拡張メソッドを使用しています。 この技術には落とし穴がないわけではありませんが、多くの場合、配列、リストなどを処理するためのルーチンコードの退屈な記述を避けることができます。



当然、JavaScriptにも同様の機能が必要です。



現在、JavaScriptにはlinq.js$ linqなど、多くのLINQ実装があります。 これらの実装は、C#実装に匹敵する幅広い機能を提供します。 裏側は、特定のアプリケーションで使用できるコードのサイズがかなり小さいためです。 たとえば、個別の関数のみが必要であり、キロバイトのサードパーティライブラリコードを追加する必要があります。



繰り返しになりますが、特定のライブラリがどのように機能するかを知りたいと思うので、大きなライブラリのソースコードを分解するのは面倒です。 または、私たちの場合、すでにJavaScript用の独自のライブラリがあり、シーケンスを操作する機能でコンパクトに補完したいだけです。



JavaScriptが実装に適している方法



計画の実装に必要なJavaScript要素を決定します。 配列があり、ラムダ関数があり、オブジェクトがあり、いくつかの注意事項があり継承が可能です。 配列に含まれるデータに対してラムダ関数で表される操作を実行し、これらの操作をオブジェクトにグループ化します。 すべて順調です。



自分で試してみましょう



必要なエンティティを定義することから始めましょう。

明らかに、イテレータはシーケンスを操作するための最も基本的なオブジェクトです。 彼に必要なのは3つの方法だけです:



そのため、ベースシーケンス(JS配列など)からイテレータを作成するメソッドが必要です。



次に、セットを操作するための一連のメソッド(where、selectなど)を含むオブジェクトが必要です。 アナログの「ヒント」ではなく、機能の本質を反映した名前にしたかったので、「Walkable」という言葉を選択しました(本質は、その要素を「歩いて」/反復できるようなものです)。 また、このオブジェクトの各メソッドは、1つのチェーンで複数の呼び出しを組み合わせる機能をサポートするためにWalkableオブジェクトを返す必要があります(つまり、前のメソッドの結果を次のメソッドに送信できるようにするため)。



第二に、データソースが必要です。 唯一のソースとして、組み込みのJS配列に限定されたくありません。 これは、上記のイテレータが便利な場所です。 シーケンスから必要なのは、イテレータを返す唯一のメソッドです(もちろん、毎回新しいインスタンスを作成して、他のイテレータの状態を変更せずに使用できるようにします)。 次のステップでは、そのようなオブジェクトにWalkableメソッドを「ミックス」できます。その結果、結果に対してWalkableメソッドを呼び出し、それらを任意のチェーンにカスケードできます。



コードで理論を説明します。 JS配列からWalkableを作成するメソッドは次のとおりです。



var arrayWalkable = function(arr) { var walker = function() { .//  -  var idx = -1; //   //     this.reset = function() { idx = -1; }; this.moveNext = function() { return (++idx < arr.length); }; this.current = function() { return (idx < 0 || idx >= arr.length) ? null : arr[idx]; }; }; //    ,   var walkable = { getWalker: function() { return new walker(); },}; //     Walkable // WAVE.extend -    ,    / // WAVE.extend(walkable, WAVE.Walkable); return walkable; }
      
      





次に、Walkableオブジェクト自体を見てみましょう。 シーケンスを操作するためのメソッドが含まれている必要があり、各メソッドは順番に新しいWalkableオブジェクトを返す必要があります。



そのため、Walkableオブジェクトと最も一般的なwhereメソッドを実装します。 名前としてwalkableを選択したため、すべての拡張メソッドを「w」の文字で呼び出します。



 wWhere: function(filter) { var srcWalkable = this; var walkable = { getWalker: function() { var walker = srcWalkable.getWalker(); return { reset: function() { walker.reset(); }, moveNext: function() { while (walker.moveNext()) { var has = filter(walker.current()); if (has) return true; } return false; }, current: function() { return walker.current(); } }; } } WAVE.extend(walkable, WAVE.Walkable); return walkable; }, //wWhere
      
      





wWhereの引数として、フィルター関数を使用します。フィルター関数は、シーケンスの要素を入力として、trueまたはfalseを返します(結果に要素を含めるかどうか)。 次に、srcWalkable変数でこれを覚えておいてください(古典的なJSトリック)。



次のステップでは、上記のarrayWalkableメソッドとの類推により、キーのgetWalkerメソッドを使用して基本の歩行可能オブジェクトを定義します。 呼び出しごとに、現在のオブジェクトの新しい反復子を取得して、他の反復子に影響を与えないようにします。 そしてオブジェクトを返します。



次に、where操作のロジックに従って反復子メソッドを再定義します。



使用例



最も簡単な使用例として、単体テストの断片を挙げることができます。



イテレータとWalkableの動作をデモンストレーションしましょう:



このような設計により、任意の長さのチェーンでWalkableを返す任意の関数を作成できることがわかります。



wWhereとの類推により、wSelect、wDistinctなど、ライブラリの残りの機能が実装されます。



 function() { var a = [1, 2, 3, 4, 5]; var aw = WAVE.arrayWalkable(a); var walker = aw.getWalker(), i=0; while(walker.moveNext()) { assertTrue(a[i] === walker.current()); i++; } assertFalse(walker.moveNext()); assertTrue(null === walker.current()); }
      
      





次に、wWhereを使用して、チェーンに配置します。



 function() { var a = [1, 2, 3, 4, 5]; var aw = WAVE.arrayWalkable(a); var wWhere1 = aw.wWhere(function(e) { return e > 1; }); var wWhere2 = wWhere1.wWhere(function(e) { return e < 5; }); var result2 = wWhere2.wToArray(); for(var i in result2) log(result2[i]); assertTrue( 3 === result2.length); }
      
      





コードは、 wv.jsに含まれるテスト用のラッパー関数を使用しました。



結論



独自のLINQに似たJavaScriptライブラリを実装するのは難しい作業ではなく、最小限のプログラミング知識を持つ人なら誰でも処理できます。



それにもかかわらず、私たちの経験では、このようなライブラリは、データ系列ロジックの処理に基づいてロジックを書くのを非常にクールにします。



ライブラリのソースコードはここから入手でき、ユニットテストはここにあります

追伸:

Walkable機能の完全なリストを追加することにしました:

wSelect、wSelectMany、wWhere、wAt、wFirst、wFirstIdx、wCount、wDistinct、wConcat、wExcept、wGroup、wGroupIntoObject、wGroupIntoArray、wGroupAggregate、wOrder、wTake、wTakeWhile、wAin、wAm、wMin、wmink、wamk、wmk、wamk、wamk、wamk、wamk、wamk、wamk、wamk、wamk、wamk、wamk、wamk、wamk、wamk、wamk、wamk、wamk、wamk、wamk、wamk、wamk、wamk、wamk、wamk、wamk、wamk、wamk、wamk、wamk、wmk、wamk、wmk wToArray、wEach、wWMA(移動平均)、wHarmonicFrequencyFilter(独自のフィルター、ここで概念が優れていることが証明されています )、wConstLinearSampling、wSineGen、wSawGen、wSquareGen、wRandomGen、



All Articles