aboutsummaryrefslogtreecommitdiff
path: root/files/ja/web/javascript/guide/iterators_and_generators/index.html
blob: ef25e5bfa8e5187720dc840be2a374cf750cac26 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
---
title: イテレーターとジェネレーター
slug: Web/JavaScript/Guide/Iterators_and_Generators
tags:
  - Guide
  - Intermediate
  - JavaScript
  - 'l10n:priority'
translation_of: Web/JavaScript/Guide/Iterators_and_Generators
---
<div>{{jsSidebar("JavaScript Guide")}} {{PreviousNext("Web/JavaScript/Guide/Using_promises", "Web/JavaScript/Guide/Meta_programming")}}</div>

<div class="summary">
<p>コレクション内の各アイテムに対する処理は非常に一般的な操作です。JavaScript では簡単な {{jsxref("Statements/for","for")}} ループから {{jsxref("Global_Objects/Array/map","map()")}}{{jsxref("Global_Objects/Array/filter","filter()")}} にいたるまで、コレクションに対する反復処理の複数の方法を提供します。</p>

<p>イテレーターとジェネレーターは、コア言語の内部に反復処理が直接的に取り入れられており、{{jsxref("Statements/for...of","for...of")}} ループの動作を簡単にカスタマイズできる仕組みをもたらします。</p>
</div>

<p>詳細についてはこちらもご覧ください:</p>

<ul>
 <li><a href="/ja/docs/Web/JavaScript/Reference/Iteration_protocols">Iteration protocols</a></li>
 <li>{{jsxref("Statements/for...of","for...of")}}</li>
 <li>{{jsxref("Statements/function*","function*")}}{{jsxref("Generator","ジェネレーター","","true")}}</li>
 <li>{{jsxref("Operators/yield","yield")}}{{jsxref("Operators/yield*","yield*")}}</li>
</ul>

<h2 id="Iterators" name="Iterators">イテレーター</h2>

<p>JavaScript では、<strong>イテレーター</strong>はシーケンスおよび潜在的には終了時の戻り値を定義するオブジェクトです。</p>

<p>より具体的に言うと、イテレーターは、次の 2 つのプロパティを持つオブジェクトを返す <code style="font-style: normal;">next()</code> メソッドを持つことによってイテレータープロトコルを実装するオブジェクトです。</p>

<dl>
 <dt><code><var>value</var></code></dt>
 <dd>反復シーケンスの次の値</dd>
 <dt><code><var>done</var></code></dt>
 <dd>シーケンスの最後の値が既に消費されている場合に <code>true</code> となります。<code><var>done</var></code> と並んで <code><var>value</var></code> が存在する場合、それがイテレーターの戻り値となります。</dd>
</dl>

<p>イテレーターオブジェクトが作成されると、<code>next()</code> を繰り返し呼び出すことによって、明示的に反復することができます。イテレーターを反復することを、イテレーターを消費すると言います。一般的に 1 回しか実行できないためです。終了値が返された後、さらに <code>next()</code> を呼び出しても、単に <code>{done: true}</code> を返し続けます。</p>

<p>Javascript で最も一般的なイテレーターは配列イテレーターで、配列の各値を順番に返します。</p>

<p>すべてのイテレーターを配列として表現できるとは想像するのは容易ですが、これは真実ではありません。配列は完全に割り当てなければなりませんが、イテレーターは必要なだけで消費されるため、0 から Infinity までの整数の範囲など、無限のサイズのシーケンスを表現できます。</p>

<p>ここでは、それを行うことができる例を示します。<code>start</code> (包括) から <code>end</code> (排他) までの一連の整数を定義する単純な範囲のイテレーターの作成を可能にします。最終的な戻り値は、作成したシーケンスのサイズあり、変数 <code><var>iterationCount</var></code> で追跡されます。</p>

<pre class="brush: js notranslate">function makeRangeIterator(<var>start</var> = 0, <var>end</var> = Infinity, <var>step</var> = 1) {
    let nextIndex = start;
    let iterationCount = 0;

    const rangeIterator = {
       next: function() {
           let result;
           if (nextIndex &lt; end) {
               result = { value: nextIndex, done: false }
               nextIndex += step;
               iterationCount++;
               return result;
           }
           return { value: iterationCount, done: true }
       }
    };
    return rangeIterator;
}
</pre>

<p>このイテレーターを使えば、次のようになります:</p>

<pre class="brush: js notranslate">const it = makeRangeIterator(1, 10, 2);

let result = it.next();
while (!result.done) {
 console.log(result.value); // 1 3 5 7 9
 result = it.next();
}

console.log("Iterated over sequence of size: ", result.value); // [5 numbers returned, that took interval in between: 0 to 10]
</pre>

<div class="note">
<p><strong>メモ:</strong> 特定のオブジェクトがイテレーターであるかどうかは考えても知ることはできません。それが必要な場合は、<a href="#Iterables">反復可能オブジェクト</a>を使用してください。</p>
</div>

<h2 id="Generators" name="Generators">ジェネレーター関数</h2>

<p>カスタムイテレーターは便利なツールですが、その作成には内部状態を明示的に維持する必要があるため、慎重なプログラミングが必要です。ジェネレーター関数は強力な代替手段を提供します。実行が連続していない単一の関数を記述することによって反復アルゴリズムを定義できます。ジェネレーター関数は、{{jsxref("Statements/function*","function*")}} 構文を使用して記述されます。</p>

<p>最初に呼び出されると、ジェネレーター関数はコードを実行せず、ジェネレーターと呼ばれるある種のイテレーターを返します。ジェネレーターの <strong>next</strong> メソッドを呼び出すことによって値が消費されると、ジェネレーター関数は <strong>yield</strong> キーワードを検出するまで実行します。</p>

<p>この関数は、必要な回数だけ呼び出すことができ、毎回新しいジェネレーターを返しますが、各ジェネレーターは 1 回のみ反復することができます。</p>

<p>上の例に適用してみましょう。このコードの動作は同じですが、実装は書くのも読むのもはるかに容易になります。</p>

<pre class="brush: js notranslate">function* makeRangeIterator(<var>start</var> = 0, <var>end</var> = 100, <var>step</var> = 1) {
    let iterationCount = 0;
    for (let i = start; i &lt; end; i += step) {
        iterationCount++;
        yield i;
    }
    return iterationCount;
}</pre>

<h2 id="Iterables" name="Iterables">反復可能オブジェクト</h2>

<p>オブジェクトは、{{jsxref("Statements/for...of", "for...of")}} 構文でループされる値など反復動作を定義する場合、<strong>反復可能オブジェクト</strong>です。{{jsxref("Array")}}{{jsxref("Map")}} のような組み込み型の中にはデフォルトの反復動作を持つものがありますが、他の型 ({{jsxref("Object")}} など) は持っていません。</p>

<p><strong>反復可能オブジェクト</strong>にするには、オブジェクトは <strong>@@iterator</strong> メソッドを実装する必要があります。つまり、オブジェクト (またはプロトタイプチェーン上のオブジェクトのうちの 1 つ) に {{jsxref("Symbol.iterator")}} キーを持つプロパティが必要です 。</p>

<p>反復可能オブジェクトは 1 回だけでも 2 回以上でも反復することができます。どちらが当てはまるかは、プログラマに任されています。</p>

<p>一度しか反復することができない反復可能オブジェクト (例えば、ジェネレーター) は、通常 <strong>@@iterator</strong> メソッドから <strong>this</strong> を返します。何度も繰り返し可能なものは、<strong>@@iterator</strong> の各呼び出しで新しいイテレーターを返す必要があります。</p>

<pre class="brush: js notranslate">function* makeIterator() {
    yield 1;
    yield 2;
}

const <var>it</var> = makeIterator();

for (const <var>itItem</var> of <var>it</var>) {
    console.log(<var>itItem</var>);
}

console.log(<var>it</var>[Symbol.iterator]() === <var>it</var>) // true;

// This example show us generator(iterator) is iterable object,
// which has the @@iterator method return the <var>it</var> (itself),
// and consequently, the <var>it</var> object can iterate only _once_.


// If we change <var>it</var>'s @@iterator method to a function/generator
// which returns a new iterator/generator object, (<var>it</var>)
// can iterate many times

<var>it</var>[Symbol.iterator] = function* () {
  yield 2;
  yield 1;
};
</pre>

<h3 id="User-defined_iterables" name="User-defined_iterables">ユーザー定義の反復可能オブジェクト</h3>

<p>以下のようにして反復可能オブジェクトを自作することができます:</p>

<pre class="brush: js notranslate">var myIterable = {
    *[Symbol.iterator]() {
        yield 1;
        yield 2;
        yield 3;
    }
}

for (let value of myIterable) {
    console.log(value);
}
// 1
// 2
// 3

or

[...myIterable]; // [1, 2, 3]
</pre>

<h3 id="Built-in_iterables" name="Built-in_iterables">組み込み反復可能オブジェクト</h3>

<p>{{jsxref("String")}}{{jsxref("Array")}}{{jsxref("TypedArray")}}{{jsxref("Map")}}{{jsxref("Set")}} はすべて組み込み反復可能オブジェクトです。これらのオブジェクトはすべて、そのプロトタイプオブジェクトに {{jsxref("Symbol.iterator")}} メソッドを備えているためです。</p>

<h3 id="Syntaxes_expecting_iterables" name="Syntaxes_expecting_iterables">反復可能オブジェクトが必要な構文</h3>

<p>{{jsxref("Statements/for...of","for-of")}} ループ、{{jsxref("Operators/yield*","yield*")}} などの文や式は、反復可能オブジェクトを必要とします。</p>

<pre class="brush: js notranslate">for (let value of ['a', 'b', 'c']) {
    console.log(value);
}
// "a"
// "b"
// "c"

[...'abc'];
// ["a", "b", "c"]

function* gen() {
  yield* ['a', 'b', 'c'];
}

gen().next();
// { value: "a", done: false }

[a, b, c] = new Set(['a', 'b', 'c']);
a;
// "a"

</pre>

<h2 id="Advanced_generators" name="Advanced_generators">高度なジェネレーター</h2>

<p>ジェネレーターは要求に応じて yield 文により生成される値を計算しており、多くの計算が必要な一連のデータを効率的に表現したり、前出のとおり無限のシーケンスを表現したりすることを可能にします。</p>

<p>ジェネレーターの内部状態を変更するのための値を {{jsxref("Global_Objects/Generator/next","next()")}} メソッドで受け入れることもできます。<code>next()</code> に渡された値は <code>yield</code> が受け取ります。最初 <code>next()</code> の呼び出しに値を渡しても常に無視されることに注意してください。</p>

<div class="blockIndicator note">
<p><strong>メモ:</strong> <code>next()</code><em>最初の</em>呼び出しに渡された値は常に無視されます。</p>
</div>

<p>以下のフィボナッチ数列ジェネレーターでは数列を再起動するのに <code>next(x)</code> を使っています:</p>

<pre class="brush: js notranslate">function* fibonacci() {
  let current = 0;
  let next = 1;
  while (true) {
    let reset = yield current;
    [current, next] = [next, next + current];
    if (reset) {
        current = 0;
        next = 1;
    }
  }
}

const sequence = fibonacci();
console.log(sequence.next().value);     // 0
console.log(sequence.next().value);     // 1
console.log(sequence.next().value);     // 1
console.log(sequence.next().value);     // 2
console.log(sequence.next().value);     // 3
console.log(sequence.next().value);     // 5
console.log(sequence.next().value);     // 8
console.log(sequence.next(true).value); // 0
console.log(sequence.next().value);     // 1
console.log(sequence.next().value);     // 1
console.log(sequence.next().value);     // 2</pre>

<p>ジェネレーターの {{jsxref("Global_Objects/Generator/throw","throw()")}} メソッドを呼び出して発生すべき例外値を渡すことで、ジェネレーターに例外を強制的に発生させることができます。これにより、まるで停止中の <code>yield</code><code>throw <em>value</em></code> 文に替わったかのように、ジェネレーターが停止した際の状況に応じて例外が発生します。</p>

<p>例外がジェネレーター内部で捕捉されない場合は、<code>throw()</code> を通してその例外が呼び出し元へと伝播し、その後 <code>next()</code> を呼び出した結果の <code>done</code> プロパティは <code>true</code> となります。</p>

<p>またジェネレーターは、与えられた値を返してジェネレーター自身の処理を終了させる {{jsxref("Global_Objects/Generator/return","return(value)")}} メソッドを持っています。</p>

<p>{{PreviousNext("Web/JavaScript/Guide/Using_promises", "Web/JavaScript/Guide/Meta_programming")}}</p>