なぜ
かつて、Webブラウザー用のグラフコンポーネントの開発中に、JavaScriptでシーケンスを処理する問題に直面しました。
C#開発者は、長年にわたってシーケンス処理に拡張メソッドを使用しています。 この技術には落とし穴がないわけではありませんが、多くの場合、配列、リストなどを処理するためのルーチンコードの退屈な記述を避けることができます。
当然、JavaScriptにも同様の機能が必要です。
現在、JavaScriptにはlinq.js 、 $ linqなど、多くのLINQ実装があります。 これらの実装は、C#実装に匹敵する幅広い機能を提供します。 裏側は、特定のアプリケーションで使用できるコードのサイズがかなり小さいためです。 たとえば、個別の関数のみが必要であり、キロバイトのサードパーティライブラリコードを追加する必要があります。
繰り返しになりますが、特定のライブラリがどのように機能するかを知りたいと思うので、大きなライブラリのソースコードを分解するのは面倒です。 または、私たちの場合、すでにJavaScript用の独自のライブラリがあり、シーケンスを操作する機能でコンパクトに補完したいだけです。
JavaScriptが実装に適している方法
計画の実装に必要なJavaScript要素を決定します。 配列があり、ラムダ関数があり、オブジェクトがあり、いくつかの注意事項があり継承が可能です。 配列に含まれるデータに対してラムダ関数で表される操作を実行し、これらの操作をオブジェクトにグループ化します。 すべて順調です。
自分で試してみましょう
必要なエンティティを定義することから始めましょう。
明らかに、イテレータはシーケンスを操作するための最も基本的なオブジェクトです。 彼に必要なのは3つの方法だけです:
- moveNext()ポインタを次の要素に移動し、成功した場合はtrueを返し、次の要素がない場合(シーケンスの終わりに達した場合)はfalseを返します。
- current()は現在の要素を返します。そのような要素がない場合はnullを返します(シーケンスの最後に到達します)。
- reset()は、ポインターをシーケンスの最初の要素に移動します。
そのため、ベースシーケンス(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操作のロジックに従って反復子メソッドを再定義します。
- リセット:シーケンスのソースをリセットするだけで呼び出します(たとえば、上記のWAVE.arrayWalkableメソッドによって生成されたオブジェクトである可能性があります。
- moveNext:フィルターの条件に一致する要素が見つかるまで、または元のシーケンスの終わりに達するまで、シーケンスのソースのmoveNextを呼び出します。
- current:シーケンスの現在のソースを呼び出します。
使用例
最も簡単な使用例として、単体テストの断片を挙げることができます。
イテレータと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、