ホームページ  >  記事  >  ウェブフロントエンド  >  stream.js 小規模で完全に独立した Javascript ライブラリ_javascript のヒント

stream.js 小規模で完全に独立した Javascript ライブラリ_javascript のヒント

WBOY
WBOYオリジナル
2016-05-16 18:00:171161ブラウズ


stream.js をダウンロード
2Kb に縮小

ストリームとは何ですか?
ストリームは、配列やリンク リストと同様に操作が簡単なデータ構造ですが、いくつかの優れた機能が追加されています。

彼らの何がそんなに特別なのでしょうか?
配列とは異なり、ストリームは魔法のようなデータ構造です。無限の数の要素を保持できます。はい、正しく聞こえました。彼の魔法は、怠惰に実行する能力から来ています。この単純な用語は、無限の数の要素をロードできることを完全に意味します。

はじめに
この記事を 10 分間読んでいただければ、プログラミングの理解が完全に変わるかもしれません (関数型プログラミングの経験がない場合は除く)。しばらくお待ちください。まず、配列またはリンク リストによく似たストリームによってサポートされる基本的な関数操作を紹介します。次に、その非常に興味深い機能をいくつか紹介します。

ストリームはコンテナです。要素を保持しています。 Stream.make を使用すると、ストリームにいくつかの要素をロードさせることができます。必要な要素をパラメータとして渡すだけです:

var s = Stream.make( 10, 20, 30 ); // s は 10、20、および 30 を含むストリームになりました
で十分です。 s は 3 つの要素 (順に 10、20、および 30) を持つストリームです。 s.length() を使用してこのストリームの長さを確認し、 s.item( i ) を使用してインデックスによって内部の要素を取得できます。 s.head() を呼び出して、このストリームの最初の要素を取得することもできます。実際に実行してみましょう:

コードをコピーします コードは次のとおりです:

var s = Stream.make ( 10, 20, 30 );
console.log( s.length() ); // 3 を出力します
console.log( s.head() ); // 10 を出力します>console.log ( s.item( 0 ) ); // 上の行とまったく同じ
console.log( s.item( 1 ) ); // 20 を出力します
console.log( s.item) ( 2 ) ) ; // 30 を出力します

このページには stream.js クラス ライブラリがロードされています。これらの例を実行したり、独自の文をいくつか書いたりしたい場合は、ブラウザの Javascript コンソールを開いて直接実行してください。

続けましょう。 new Stream() を使用するか、Stream.make() を直接使用して空のストリームを構築することもできます。 s.tail() メソッドを使用すると、最初の要素を除くストリーム内の残りの要素をすべて取得できます。空のストリームで s.head() メソッドまたは s.tail() メソッドを呼び出すと、例外がスローされます。ストリームが空かどうかは、true または false を返す s.empty() を使用して確認できます。

コードをコピーします コードは次のとおりです。
var s = Stream.make( 10, 20, 30 );
var t = s.tail(); // 20 と 30 を含むストリームを返します。
console.log( t.head() ) // 20 var u = t.tail(); // 1 つの項目を含むストリームを返します: 30
console.log( u.head() ) // 30
var v = u.tail(); ; // 空のストリームを返します
console.log( v.empty() ); // true を出力します


これを実行すると、ストリーム内のすべての要素が出力されます:


コードをコピーします コードは次のとおりです:
var s = Stream.make( 10, 20, 30 ) ;
while ( !s.empty() ) {
console.log( s.head() );
s = s.tail(); 🎜>これを達成するための簡単なメソッドがあります: s.print() はストリーム内のすべての要素を出力します。

他に何に使えますか?
もう 1 つの便利な関数は、Stream.range( min, max ) 関数です。最小値から最大値までの自然数を含むストリームを返します。



コードをコピーします

コードは次のとおりです: var s = Stream.range( 10, 20 ); s.print(); // 10 から 20 までの数値を出力します このストリームでは、マップ、フィルター、ウォークなどの関数を使用できます。 s.map( f ) は関数であるパラメータ f を受け取ります。ストリーム内のすべての要素は f によって処理され、その戻り値はこの関数によって処理されたストリームです。たとえば、これを使用してストリーム内の数値を 2 倍にすることができます。
function doubleNumber( x ) {
return 2 * x;
}

varnumbers = Stream.range( 10, 15 );
numbers.print(); // 10、11、12、13、14、15 を出力します。
var doubles =numbers.map( doubleNumber ); print(); // 20、22、24、26、28、30 を出力します


素敵ですね。同様に、 s.filter( f ) も関数であるパラメーター f を受け入れます。ストリーム内のすべての要素はこの関数によって処理されますが、戻り値には f 関数が返すことを許可する要素のみが含まれます。真実。 。したがって、これを使用して、ストリーム内の特定の要素をフィルター処理できます。このメソッドを使用して、前のストリームに基づいて奇数のみを含む新しいストリームを構築しましょう:
コードをコピーします コードは次のとおりです。

function checkIfOdd( x ) {
if ( x % 2 == 0 ) {
// 偶数
return false;
else {
// 奇数
return true;
varnumbers = Stream.range( 10, 15 ); // 出力します。 10, 11, 12, 13, 14, 15
varonlyOdds =numbers.filter( checkIfOdd );
onlyOdds.print(); // 11, 13, 15 を出力します


とても効果的ですね。最後の s.walk(f) メソッドは、関数であるパラメーター f も受け入れます。ストリーム内のすべての要素はこの関数によって処理される必要がありますが、ストリームには影響しません。ストリーム内のすべての要素を出力するという私たちのアイデアには、新しい実装方法があります:



コードをコピー
コードは次のとおりです: function printItem( x ) { console.log( '要素は次のとおりです: ' x );
}
var数値 = Stream.range( 10, 12 ); 🎜>// prints :
// 要素は次のとおりです: 10
// 要素は次のとおりです: 11
// 要素は次のとおりです: 12
numbers.walk( printItem ); 🎜>
非常に便利な関数 s.take(n) もあります。これは、元のストリームの最初の n 要素のみを含むストリームを返します。これはストリームのインターセプトに使用すると便利です:




コードをコピー


コードは次のとおりです: その他の便利な機能: s.scale(actor) は、ストリーム内のすべての要素に係数 (factor) を乗算します。 s.add( t ) は、ストリーム s とストリーム t の各要素を作成します。の要素が加算され、加算結果が返されます。いくつかの例を見てみましょう:




コードをコピー


コードは次のとおりです:
var 番号= Stream .range( 1, 3 ); var multiplesOfTen =numbers.scale( 10 ); multiplesOfTen.print(); // 10, 20, 30 numbers.add( multiplesOfTen ); print( ); // 11、22、33 を出力します
これまで見てきたのは数値に対する操作ですが、ストリームには文字列、ブール値、関数、オブジェクトなど、あらゆるものを読み込むことができます。他の配列やストリームでも。ただし、ストリームは一部の特殊な値 (null および unknown) をロードできないことに注意してください。

あなたの魔法を見せてください!
さて、無限を扱いましょう。ストリームに無限の要素を追加する必要はありません。たとえば、Stream.range(low, high) メソッドでは、2 番目のパラメータを無視して Stream.range(low) と記述することができます。この場合、データには上限がないため、ストリームは All でロードされます。小さい値から無限大までの自然数。 low パラメータを無視することもできます。このパラメータのデフォルト値は 1 です。この場合、Stream.range() はすべての自然数を返します。

これには無限のメモリ、時間、処理能力が必要ですか?
いいえ、そんなことはありません。これが最高の部分です。このコードを実行すると、通常の配列と同様に非常に高速に実行されます。 1 から 10 までを出力する例を次に示します。




コードをコピーします


コードは次のとおりです:
var NaturalNumbers = Stream.range(); // 1 以降のすべての自然数を含むストリームを返します var oneToTen = NaturalNumbers.take( 10 ); // 数値 1 を含むストリームを返します... 10 oneToTen.print();
あなたは嘘をついています
はい、私は嘘をついています。重要なのは、これらの構造を無限と考えることができるということです。これにより、シンプルさを追求した新しいプログラミング パラダイムが導入され、コードが通常の命令型プログラミングよりも理解しやすくなり、自然数学に近づきます。 Javascript ライブラリ自体は非常に短く、このプログラミング パラダイムに従って設計されています。これをさらに活用してみましょう。1 つはすべての奇数用、もう 1 つはすべての偶数用の 2 つのストリームを構築します。
コードをコピーします コードは次のとおりです。

var NaturalNumbers = Stream.range(); // NaturalNumbers は 1, 2, 3, ...
var EvenNumbers = NaturalNumbers.map( function ( x ) {
return 2 * x;
} ); // EvenNumbers は 2, 4, 6 , ...
var oddNumbers = NaturalNumbers.filter( function ( x ) {
return x % 2 != 0;
} ); // oddNumbers は 1, 3, 5, ...
evenNumbers.take( 3 ).print(); // 2、4、6 を出力します
oddNumbers.take( 3 ).print(); // 1、3、5 を出力します

素敵ですね。誇張ではなく、ストリームは配列よりも強力です。さて、少しお待ちください。ストリームについてもう少し詳しく説明させていただきます。 new Stream() を使用して空のストリームを作成し、 new Stream( head, functionReturningTail ) を使用して空ではないストリームを作成できます。この空ではないストリームの場合、渡す最初のパラメータはストリームの先頭要素になり、2 番目のパラメータはストリームの末尾 (残りのすべての要素を含むストリーム) を返す関数になります。おそらく空のストリームです。 。混乱した?例を見てみましょう:
コードをコピー コードは次のとおりです:

var s = new Stream ( 10, function () {
return new Stream();
} );
// s ストリームの先頭は 10; s ストリームの末尾は空のストリームです
s .print(); // 10 を出力します
var t = new Stream( 10, function () {
return new Stream( 20, function () {
return new Stream( 30, function ( ) {
return new Stream();
} );
} );
// t ストリームの先頭は 10 です。 20 と末尾
// 先頭は 30、末尾は空のストリームです
t.print() // 10、20、30 を出力します

何も問題はありませんか? Stream.make(10, 20, 30) を使用してこれを直接行うことができます。ただし、この方法で無限ストリームを簡単に構築できることに注意してください。無限ストリームを作成しましょう:


関数 1( ) {
return new Stream(
// 1 のストリームの最初の要素は 1...
1,
// このストリームの残りの要素は呼び出しによって与えられます関数 ones() (これと同じ関数です!)
ones
);

var s = ones(); // s には 1, 1, 1, 1, が含まれます。 ...
s.take( 3 ).print(); // 1, 1, 1 を出力します


無限ストリーム .print() で s を使用する場合、延々と印刷され、最終的にはメモリが使い果たされます。したがって、 s.print() を使用する前に s.take( n ) を使用することをお勧めします。無限ストリームで s.length() を使用することも無意味であるため、これらの操作を実行しないでください。無限ループ (無限ストリームの最後に到達しようとする) が発生します。ただし、無限ストリームの場合は、 s.map( f ) と s.filter( f ) を使用できます。ただし、s.walk(f) も無限ストリームには役に立ちません。全体として、覚えておくべきことがいくつかあります。無限ストリームの場合は、必ず s.take(n) を使用して有限部分を取り出してください。
もっと面白いことができないか見てみましょう。自然数を含むストリームを作成する興味深い方法もあります。




コードをコピーします コードは次のとおりです。 関数ones() {
新しいストリームを返す( 1, ones );
}
関数naturalNumbers() {
新しいストリームを返す(
//自然数は最初の要素が 1 であるストリームです...
1,
function () {
// 残りはすべて 1 ずつ増分された自然数です
// これが得られます自然数のストリームを追加することで...
// 1, 2, 3, 4, 5, ...
// 1 の無限のストリームに...
// 1, 1 , 1, 1, 1, ...
// 結果は...
// 2, 3, 4, 5, 6, ...
// これらは確かに自然の残りの部分です1 つ後の数値
returnones().add(naturalNumbers());
}
naturalNumbers().take( 5 ).print(); 1、2、3、4、5


注意深く読んでいる人は、新しく構築されたストリームの 2 番目のパラメータが、末尾そのものではなく末尾を返す関数である理由がわかるでしょう。この方法は、末尾インターセプト操作を遅らせることによって、無限の実行サイクルを防ぎます。

より複雑な例を見てみましょう。以下は読者のために残された演習です。次のコードが何をするのか教えてください。
コードをコピーします コードは次のとおりです。

function sieve( s ) {
var h = s.head();
return new Stream( h, function () {
return sieve( s.tail().filter( function( x ) {
return x % h != 0;
} ) );
} );
}
sieve( Stream.range( 2 ) ).take( 10 ).print();時間をかけてこのコードの目的を理解してください。関数型プログラミングの経験がない限り、ほとんどのプログラマはこのコードを理解するのが難しいと感じるでしょう。そのため、コードがすぐに分からなくてもイライラしないでください。ちょっとしたヒント: 印刷されるストリームのヘッダー要素が何であるかを調べてください。次に、2 番目の要素 (残りの要素の先頭の要素) が何であるかを調べ、次に 3 番目の要素、次に 4 番目の要素を調べます。関数の名前からもヒントが得られます。
このようなパズルに興味がある場合は、
ここにさらにいくつかあります

このコードが何をするのか本当に理解できない場合は、実行して自分の目で確認してください。こうすることで、どのように行われるかを簡単に理解できます。
トリビュート
ストリームは実際には新しいアイデアではありません。多くの関数型プログラミング言語がこの機能をサポートしています。いわゆる「ストリーム」は Scheme 言語での名前であり、Scheme は LISP 言語の方言です。 Haskell 言語は、無限大のリストもサポートしています。 「take」、「tail」、「head」、「map」、「filter」という名前はすべて Haskell 言語に由来しています。この概念は、異なりますが非常に似ており、Python や他の多くの中国語にも存在し、それらはすべて「ジェネレーター」と呼ばれます。

これらのアイデアは、関数型プログラミング コミュニティで長い間広まっていました。ただし、ほとんどの JavaScript プログラマ、特に関数型プログラミングの経験のないプログラマにとって、これは非常に新しい概念です。

ここでの例やアイデアの多くは、書籍
Structure and Interpretation of Computer Programs
から引用しています。これらのアイデアが気に入った場合は、この本をオンラインで無料で読むことを強くお勧めします。これは、この Javascript ライブラリを開発する際の私の創造性の源でもあります。
他の構文形式のストリームが好きなら、
linq.js
を試すことができます。node.js を使用する場合は、

node-lazy の方が適しているかもしれません。 CoffeeScript が気に入った方は、Michael Blume が stream.js を CoffeeScript に移植して CoffeeStream を作成しています。
読んでいただきありがとうございます!
役に立つものを見つけて、stream.js を楽しんでいただければ幸いです。図書館は無料ですので、気に入っていただけた場合、または何らかの形で役に立った場合は、私にホット チョコレート ドリンクを買ってきていただくか (私はコーヒーを飲みません)、電話で連絡していただけると幸いです。これを行う予定がある場合は、どこの出身で、何をしているのかを示してください。私は世界中の写真を集めるのが大好きなので、あなたの街の写真も含めてください。

声明:
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。