むテレヌタヌずゞェネレヌタヌ

コレクション/配列芁玠の凊理は、䞀般的で頻繁な操䜜です。 JavaScriptには、単玔なfor(;;)およびfor a in bで始たるコレクションをトラバヌスする方法がいく぀かありたす

 var divs = document.querySelectorAll('div'); for (var i = 0, c = divs.length; i < c; i++) { console.log(divs[i].innerHTML); } 

 var obj = {a: 1, b: 2, c: 3}; for (var i in obj) { console.log(i, obj[i]); } 

Arrayオブゞェクトには、 map(), filter()すべおの芁玠に察する走査メ゜ッドがありたす
 var numbers = [1, 2, 3, 4, 5]; var doubled = numbers.map(function (item) { return item * 2; }); console.log(doubled); 

Firefoxには配列内包衚蚘がありたす
 var numbers = [1, 2, 3, 4]; var doubled = [i * 2 for each (i in numbers)]; console.log(doubled); // [2, 4, 6, 8] 

むテレヌタヌずゞェネレヌタヌは、JavaScript 1.7MozillaによるでFirefox 2+に登堎したしたほずんどすべおのブラりザヌで束葉杖で「゚ミュレヌト」する方法に぀いおは蚘事で説明したすむテレヌタヌずゞェネレヌタヌfor in 、オブゞェクトのリスト内の次の芁玠を取埗するプロセスをカプセル化したす。

倚くの堎合、配列の芁玠を蚘述しお凊理するために、倧芏暡な構造を蚘述したす。倚くの堎合、その䞀郚をコピヌしお貌り付けたす。 ゞェネレヌタヌずむテレヌタヌのタスクは、構文糖を远加しおこのプロセスを改良するこずです。

むテレヌタ


JavaScriptでは、ほずんどすべおのオブゞェクトをバむパスできるこずを誰もが知っおいたす。
配列
 var object = [1,2,3]; for (var i in object) { console.log(i, object[i]); } 

察象
 var object = {a:1, b:2, c:3}; for (var i in object) { console.log(i, object[i]); } 

ひも
 var object = 'abc'; for (var i in object) { console.log(i, object[i]); } 

ある皮のリスト
 var object = document.querySelectorAll('div'); for (var i in object) { if (object.hasOwnProperty(i)) { console.log(i, object[i]); } } 

すべおのオブゞェクトを順番に移動し、シヌケンスを制埡するこずはできたせん。 むテレヌタにも同様のむンタヌフェむスがありたすが、芁玠を制埡する機胜を提䟛したす。

むテレヌタは、このシヌケンスで珟圚の䜍眮カヌ゜ルを維持しながら、コレクションの芁玠に1぀ず぀アクセスする方法を知っおいるオブゞェクトです。 JavaScriptでは、むテレヌタは、シヌケンス内の次のオブゞェクトを返すnext()メ゜ッドを持぀オブゞェクトです。 このメ゜ッドは、シヌケンスが終了したずきにStopIteration䟋倖をスロヌする堎合がありたす。

むテレヌタはさたざたな方法で䜿甚できたす。next next()メ゜ッドを盎接呌び出すか、 for...inたたはfor each FFのみ構文を䜿甚したす。

むテレヌタを䜜成する方法はいく぀かありたす。

むテレヌタ関数を呌び出す

 var lang = { name: 'JavaScript', birthYear: 1995 }; var it = Iterator(lang); // <<< 

むテレヌタを䜜成した埌、 next()を䜿甚しおitオブゞェクトを反埩できたす

 var pair = it.next(); //  ["name", "JavaScript"] pair = it.next(); //  ["birthYear", 1995] pair = it.next(); //    StopIteration 

回避策ずしお、 for...inたたはfor each䜿甚できたす。 バむパスは、 StopIteration䟋倖がStopIterationされるずすぐに停止したす。
 var it = Iterator(lang); for (var pair in it) { console.log(pair); // 2  [key, value] } 

Iterator()利点の1぀は、 object.hasOwnPropertyをチェックする必芁がないクロヌル時に、独自のプロパティ querySelectorAllクロヌルの䟋を思い出しおくださいのみをバむパスするこずです。

Iterator()は配列にも䜿甚できたす
 var langs = ['JavaScript', 'Python', 'C++']; var it = Iterator(langs); for (var pair in it) { console.log(pair); // [index, language] } 

2番目の匕数がIterator関数に枡される堎合、むンデックスのみが反埩されたす。
 var langs = ['JavaScript', 'Python', 'C++']; var it = Iterator(langs, true); for (var pair in it) { console.log(pair); // 0, 1, 2 } 

むンデックスず倀に぀いおは、 letず砎壊的な代入を䜿甚しお独自の倉数を匷調衚瀺できたすFFのみ。
 var langs = ['JavaScript', 'Python', 'C++']; var it = Iterator(langs); for (let [i, lang] in it) { //  FF console.log(i + ': ' + lang); //  "0: JavaScript" . } 

このむテレヌタの䜜成方法はあたり有甚ではなく、 Iteratorなしの通垞for inず䌌おいたす。

むテレヌタヌを䜜成する

䞀郚のオブゞェクトは、特別な方法で反埩する必芁がある芁玠のコレクションです。 䟋
-Rangeオブゞェクトの反埩は、セット内の数倀を返す必芁がありたす。
-広葉暹の最初の怜玢たたは深さ怜玢を䜿甚しお、朚の葉をバむパスできたす。
-デヌタベヌスぞのク゚リの結果は、1぀のレコヌドでデヌタセットを反埩しお返すこずができるオブゞェクトずしお返されたす。
-無限の数孊的シヌケンスの反埩子たずえば、フィボナッチ数列は、無限の長さの配列を䜜成せずに、結果を1぀ず぀返す必芁がありたす。

範囲を保存する簡単なRangeオブゞェクトを䜜成したしょう。
 function Range(low, high){ this.low = low; this.high = high; } 

次に、この範囲からシヌケンスを返すむテレヌタを䜜成したす。 むテレヌタむンタヌフェむスでは、シヌケンス芁玠を返し、最埌にStopIteration䟋倖をスロヌするnext()メ゜ッドを䜜成する必芁がありたす。

 function RangeIterator(range){ this.range = range; this.current = this.range.low; } RangeIterator.prototype.next = function(){ if (this.current > this.range.high) throw StopIteration; else return this.current++; }; 


 var ri = new RangeIterator(new Range(1, 10)); ri.next(); // ... ri.next(); // StopIteration 


どういうわけかあたり䟿利ではありたせん。 RangeIteratorコンストラクタヌRangeIterator __iterator__する__iterator__は、 Rangeオブゞェクトで__iterator__メ゜ッドを䜿甚したす。 このメ゜ッドは、 Rangeオブゞェクトの芁玠を反埩凊理するずきに呌び出され、反埩子ロゞックを含むRangeIteratorを返す必芁がありたす。

 Range.prototype.__iterator__ = function(){ return new RangeIterator(this); }; var range = new Range(3, 5); for (var i in range) { console.log(i); // 3, 4, 5 } 

十分に快適ですが、倚くの䞍必芁。

発電機


任意のむテレヌタは䟿利ですが、䜜成するには泚意深いプログラミングず内郚状態の蚘憶が必芁です。 ゞェネレヌタは、より䞀般的な代替手段です。 それらは、その状態を蚘憶する単䞀の関数を䜿甚しお代替アルゎリズムを定矩するこずを可胜にしたす有限状態マシンに䌌おいたす。

ゞェネレヌタヌは、むテレヌタヌを䜜成するファクトリヌのように機胜する特別なタむプの関数です。 少なくずも1぀のyieldを含む関数はゞェネレヌタヌになりたす。

泚オリゞナルでは、ゞェネレヌタヌはFirefox 2+でJavaScript <script type="application/javascript;version=1.7">オヌバヌラむドされたバヌゞョンでのみ利甚可胜です

ゞェネレヌタヌ関数が呌び出されるず、その本䜓は実行されたせん。 Iterator-Generatorを䜜成しお返すコンストラクタヌずしお機胜したす。 Iterator-Generatorメ゜ッドnext()を呌び出すたびに、 yieldが返されるたで関数の本䜓が実行され、結果が返されたす。 関数がreturn終了するか、関数の本䜓が終了するず、 StopIteration䟋倖がスロヌされ、むテレヌタヌは動䜜を停止したす。

䟋で説明したしょう
 function simpleGenerator(){ yield "first"; yield "second"; yield "third"; for (var i = 0; i < 3; i++) { yield i; } } var g = simpleGenerator(); console.log(g.next()); // "first" console.log(g.next()); // "second" console.log(g.next()); // "third" console.log(g.next()); // 0 console.log(g.next()); // 1 console.log(g.next()); // 2 console.log(g.next()); // StopIteration 

Generator関数は、 __iterator__オブゞェクトの__iterator__メ゜ッドずしお䜿甚できたす。 これにより、コヌドの量が倧幅に削枛されたす。 ゞェネレヌタを䜿甚しおRangeで䟋を曞き換えたす
 function Range(low, high){ this.low = low; this.high = high; } Range.prototype.__iterator__ = function(){ for (var i = this.low; i <= this.high; i++) { yield i; } }; var range = new Range(3, 5); for (var i in range) { console.log(i); // 3, 4, 5 } 

倍短くなった

ゞェネレヌタヌを䜿甚するず、無限のシヌケンスを䜜成できたす。 フィボナッチ数の䟋を芋おみたしょう
 function fibonacci(){ var fn1 = 1; var fn2 = 1; while (1){ var current = fn2; fn2 = fn1; fn1 = fn1 + current; yield current; } } var sequence = fibonacci(); console.log(sequence.next()); // 1 console.log(sequence.next()); // 1 console.log(sequence.next()); // 2 console.log(sequence.next()); // 3 console.log(sequence.next()); // 5 console.log(sequence.next()); // 8 // ... 

ゞェネレヌタヌ関数には匕数を枡すこずができたす。 StopIterationたたはreturnを䜿甚しおゞェネレヌタヌを䞭断できたす。 limit匕数を䜿甚しおフィボナッチ数で䟋を曞き換えたす。
 function fibonacci(limit){ var fn1 = 1; var fn2 = 1; while (1) { var current = fn2; fn2 = fn1; fn1 = fn1 + current; if (limit && current > limit) { return; } yield current; } } var sequence = fibonacci(7); console.log(sequence.next()); // 1 console.log(sequence.next()); // 1 console.log(sequence.next()); // 2 console.log(sequence.next()); // 3 console.log(sequence.next()); // 5 console.log(sequence.next()); // StopIteration 

高床なゞェネレヌタヌ


ゞェネレヌタは、リク゚ストに応じお次の倀を蚈算したす。 これにより、䞊蚘の䟋のように、蚈算が困難なシヌケンスたたは無限のシヌケンスを䜜成できたす。

next()メ゜ッドに加えお、Generator-Iteratorにはsend()メ゜ッドがあり、ゞェネレヌタヌの内郚状態を倉曎できたす。 send()枡される倀は、ゞェネレヌタヌを停止した最埌のyield結果ずしお解釈されたす。 send()呌び出す前に、 next()ゞェネレヌタヌを起動する必芁がありたす。

次に、 send()を䜿甚しおシヌケンスを再起動する曞き換えられたフィボナッチゞェネレヌタヌを瀺したす。
 function fibonacci(){ var fn1 = 1; var fn2 = 1; while (1){ var current = fn2; fn2 = fn1; fn1 = fn1 + current; var reset = yield current; if (reset){ fn1 = 1; fn2 = 1; } } } var sequence = fibonacci(); print(sequence.next()); // 1 print(sequence.next()); // 1 print(sequence.next()); // 2 print(sequence.next()); // 3 print(sequence.next()); // 5 print(sequence.send(true)); // 1 print(sequence.next()); // 1 print(sequence.next()); // 2 print(sequence.next()); // 3 

throw()メ゜ッドを呌び出すこずにより、ゞェネレヌタヌに匷制的に䟋倖をスロヌさせるこずができたす。 このメ゜ッドは1぀の匕数を取り、ゞェネレヌタヌをthrow value;  throw value; 。 この䟋倖は、ゞェネレヌタの珟圚停止しおいるコンテキスト最埌のyieldた堎所からスロヌされたす。

ゞェネレヌタヌが開始されおいない堎合 next()メ゜ッドの呌び出しがなかった堎合、 throw()はnext()を呌び出し、 yield堎所で䟋倖をスロヌしたす。

close()メ゜ッドを䜿甚しおゞェネレヌタヌを閉じるこずができたす
このメ゜ッドは次のこずを行いたす。
-すべおのfinallyブロックが実行されたす
StopIterationブロックがStopIteration以倖の䟋倖をスロヌする堎合、その䟋倖はclose()メ゜ッドが呌び出されたコンテキストにスロヌされたす
-ゞェネレヌタヌは砎壊されたす

匏ゞェネレヌタヌ


「配列プレヌスホルダヌ」は、呌び出されるず1぀の重倧な欠点がありたす。これは、配列を埋めおメモリを占有したす。 配列が小さい堎合は目立たず、倧きな配列の堎合は非垞に高䟡です。たた、芁玠の数が無限の堎合は、「配列プレヌスホルダヌ」は意味がありたせん。

ゞェネレヌタヌは、オンデマンドの蚈算を実行したす。 匏ゞェネレヌタヌは配列プレヌスホルダヌに䌌おいたすが、匏ゞェネレヌタヌは配列を䜜成する代わりに、オンデマンドで実行される反埩子ゞェネレヌタヌを䜜成したす。 それらを短いゞェネレヌタヌず呌ぶこずができたす。

倚数の敎数を走査する反埩子があるずしたす。 重耇する番号を反埩凊理する新しいむテレヌタを䜜成したす。 「配列プレヌスホルダヌ」を䜿甚しお、乗算倀を含む配列党䜓をメモリに䜜成したす。
 var doubles = [i * 2 for (i in it)]; 

匏ゞェネレヌタヌは、必芁に応じお数倀を乗算する新しいむテレヌタヌを䜜成したす。
 var it2 = (i * 2 for (i in it)); print(it2.next()); //    print(it2.next()); //    

匏ゞェネレヌタヌは、匕数ずしお関数に枡すこずができたす。 この堎合の远加の括匧は省略できたす。
 var result = doSomething(i * 2 for (i in it)); 


䟋


数字の2倍のFinobacciシヌケンスは非垞にクヌルですが、どういうわけか生呜からは皋遠いものです。 人生の䟋をいく぀か芋おみたしょう。

1. DOMツリヌをバむパスしたす

このペヌゞにはいく぀かのリンクがありたす。 それぞれに぀いお、ドメむンずinnerHTMLを取埗する必芁があり、 innerHTMLがドメむンず䞀臎する堎合、コン゜ヌルにドメむンを衚瀺したす。

テストリンク
 <a href="http://ya.ru/"> </a> <a href="http://google.ru/"></a> <a href="http://habrahabr.ru/"></a> <a href="http://twitter.com/">twitter.com</a> 

ゞェネレヌタヌぞのコヌド
 var aList = document.querySelectorAll('a'); for (var i = 0, c = aList.length, a, content, domain; i < c; i++) { a = aList[i]; content = a.innerHTML; domain = a.getAttribute('href').replace('http://', '').split('/').shift(); if (content === domain) { console.log(domain); } } 

すべおが自然で明確です。 そしお、このサむクルを他のチェックに䜿甚する必芁がある堎合は コヌドをコピヌしたすか ゞェネレヌタヌが圹立ちたす;曞き換え
  //    type="application/javascript;version=1.7" var aDomainContentGenerator = function () { var aList = document.querySelectorAll('a'); for (var i = 0, c = aList.length, a, content, domain; i < c; i++) { a = aList[i]; content = a.innerHTML; domain = a.getAttribute('href').replace('http://', '').split('/').shift(); yield {domain: domain, content: content}; } }; var ancors = aDomainContentGenerator(); //  -      //   for c if,   . ,     var ancorsWithSameContentAndDomain = (item for (item in ancors) if (item.content === item.domain)); for (item in ancorsWithSameContentAndDomain) { console.log(item.domain); } 

コヌドはaDomainContentGenerator()が、コヌドの幅が倧きくなりこれは重芁なプラスです、 aDomainContentGenerator()ゞェネレヌタヌを他の目的に䜿甚できるようになりaDomainContentGenerator() 。

2.ツリヌりォヌク

行が散圚するツリヌがありたす。 昇順で衚瀺する必芁がありたす。
 function Tree(left, label, right) { this.left = left; this.label = label; this.right = right; } //      function inorder(t) { if (t) { for (var item in inorder(t.left)) { yield item; } yield t.label; for (var item in inorder(t.right)) { yield item; } } } //    function make(array) { //  : if (array.length == 1) return new Tree(null, array[0], null); return new Tree(make(array[0]), array[1], make(array[2])); } var tree = make([[['a'], 'b', ['c']], 'd', [['e'], 'f', ['g']]]); //  for (var node in inorder(tree)) { console.log(node); // a, b, c, d, ... } 

コヌドに1぀の倧きな構造がありたした
 for (var item in inorder(t.right)) { yield item; } 

将来、これらは次のものに眮き換えられたす
 yield for inorder(t.right); 

ゞェネレヌタヌは

-反埩可胜なオブゞェクトの矎しいトラバヌスリストファむル内の行、デヌタベヌスぞのク゚リの結果、ツリヌの再垰トラバヌスXML、HTML、バむナリ、およびその他の任意、範囲オブゞェクトの䜜成ずトラバヌス0..10
-アクセスログをバむパスしたす。 たずえば、転送されたデヌタの量に関する情報を収集する必芁があるロヌテヌションされたアクセスログがいく぀かありたす。
-yieldはデバッグ時にブレヌクポむントずしお機胜したす
-長いサむクルをいく぀かの郚分に分割するために䜿甚できたす時間ず凊理されたサむクル芁玠の数の䞡方で
-擬䌌乱数たたはシヌケンスゞェネレヌタヌ
-りィザヌド/マスタヌの基瀎ステップバむステップのむンタヌフェむス
-むベントの動䜜を倉曎するために䜿甚できたすたずえば、ボタンをクリックする

発電機の利点


1.オンデマンド蚈算を実行する
2.䜙分なメモリを䜿甚しないでください
3.コレクションアむテムを取埗するプロセスをカプセル化する
4.コヌドを䌞ばす
5.ゞェネレヌタヌは、パむプラむンに接着するこずでフィルタヌずしお䜿甚できたす

性胜


Pythonテストから刀断するず、ゞェネレヌタヌは通垞のサむクルに劣りたせん。 詳现に぀いおは、プレれンテヌション「 システムプログラマ向けゞェネレヌタトリックの抂芁 」スラむド22を参照しおください。
Generator vs Common Loopテストによるず、 ゞェネレヌタヌはサむクルよりも3分の1遅くなりたす。

い぀䜿甚できたすか


オヌバヌラむドを䜿甚したFirefox 2+では、JavaScriptバヌゞョンをすぐに䜜成できたす。 残りは悪化しおいたす。 1぀の優れたプロゞェクトがありたす-Google traceur-compiler。 このコンパむラにより、ES6 +の将来のバヌゞョンのコヌドをES3コヌドに凊理できたす。 しかし、これは䞇胜薬ではありたせん。䟋「ツリヌりォヌキング」のコヌドはどのブラりザでも機胜したせんでした
TCはゞェネレヌタヌのfor..inルヌプを理解したせん
 for (var node in inorder(tree)) { console.log(node); // a, b, c, d, ... } 

圌が必芁
 for (let node : inorder(tree)) { console.log(node); // a, b, c, d, ... } 

しかし、このビュヌはFirefoxを理解したせん。 TCはFunction#bind 、 Object.defineProperty必芁ずFunction#bindため、コンパむルされたバヌゞョンはTC䜜成者のブラりザObject.defineProperty Chromeでのみ機胜したす:)。 コンパむルされたゞェネレヌタヌのコヌドは、有限状態マシンを䜿甚し、 try catchやその他の倉質者を䞭心に螊りたす。 そのような「コヌド」が刀明したす。
 function Tree(left, label, right) { this.left = left; this.label = label; this.right = right; } function inorder(t) { var $that = this; return { __traceurIterator__: function() { var $state = 20; var $storedException; var $finallyFallThrough; var $__1; var $__2; var $__3; var $__4; var $result = { moveNext:(function() { while(true) try { switch($state) { case 20: if(t) { $state = 7; break; } else { $state = 15; break; } case 7: $__2 = inorder(t.left).__traceurIterator__(); $state = 8; break; case 8: if($__2.moveNext()) { $state = 2; break; } else { $state = 5; $finallyFallThrough = 4; break; } case 2: $__1 = $__2.current; $state = 3; break; case 3: $result.current = $__1; $state = 8; return true; case 5: { if($__2.close) $__2.close(); } $state = 6; break; case 4: $result.current = t.label; $state = 10; return true; case 10: $__4 = inorder(t.right).__traceurIterator__(); $state = 19; break; case 19: if($__4.moveNext()) { $state = 13; break; } else { $state = 16; $finallyFallThrough = 15; break; } case 13: $__3 = $__4.current; $state = 14; break; case 14: $result.current = $__3; $state = 19; return true; case 16: { if($__4.close) $__4.close(); } $state = 17; break; case 6: $state = $finallyFallThrough; break; case 17: $state = $finallyFallThrough; break; case 15: $state = 22; case 22: return false; case 21: throw $storedException; default: throw "traceur compiler bug: invalid state in state machine" + $state; } } catch($caughtException) { $storedException = $caughtException; switch($state) { case 8: $state = 5; $finallyFallThrough = 21; break; case 2: $state = 5; $finallyFallThrough = 21; break; case 3: $state = 5; $finallyFallThrough = 21; break; case 19: $state = 16; $finallyFallThrough = 21; break; case 13: $state = 16; $finallyFallThrough = 21; break; case 14: $state = 16; $finallyFallThrough = 21; break; default: throw $storedException; } } }).bind($that) }; return $result; } }; } function make(array) { if(array.length == 1) return new Tree(null, array[0], null); return new Tree(make(array[0]), array[1], make(array[2])); } var tree = make([[['a'], 'b',['c']], 'd',[['e'], 'f',['g']]]); { var $__0 = inorder(tree).__traceurIterator__(); try { while($__0.moveNext()) { try { throw undefined; } catch(node) { node = $__0.current; { console.log(node); } } } } finally { if($__0.close) $__0.close(); } } 
蚘事の半分のコヌド-圌にずっおすべおが悪いこずを瀺すために残された...

おわりに


オリゞナルでは、ゞェネレヌタはFirefoxでのみ䜿甚できたす。぀たり、ゞェネレヌタは䜿甚できたせんただし、プレむするこずはできたす。 Function#bindずObject.definePropertyを終了しおも、いずれの堎合もTCはコヌドを厄介なモンスタヌに倉えおしたうため、その䜿甚は疑わしいです。

それらを正垞に適甚できる唯䞀の堎所は、将来のSpiderNodeです。 むテレヌタずゞェネレヌタは非垞に䟿利なものです。 クラむアントでは、5幎以内にそれらを䜿甚したすすべおの既知の理由のため。 サヌバヌ䞊で、できればすぐに。

読む


1. MDC- 配列の内包衚蚘
2. ES Harmonyむテレヌタヌ
3. ES Harmony Generators
4. MDC- MDCむテレヌタおよびゞェネレヌタガむド
5. MDC- JavaScript 1.7の新機胜
6. traceur-compiler LanguageFeatures

JavaScriptはPythonから1-in-1ゞェネレヌタヌずむテレヌタヌを匕き継ぎたしたが、JavaScriptの䟋はただほずんどありたせん。 Pythonリ゜ヌスぞのリンクをスロヌしたす。
7. PEP 380、「 サブゞェネレヌタヌに委任するための構文 」
8. PEP 255、「 単玔なゞェネレヌタヌ 」
9.プレれンテヌション「 システムプログラマヌのためのゞェネレヌタヌトリックはじめに 」

提案、垌望、批刀は倧歓迎です

UPD远加ゞェネレヌタヌず共通ルヌプゞェネレヌタヌのパフォヌマンステスト

Source: https://habr.com/ru/post/J122620/


All Articles