「ジェネレータ (プログラミング)」の版間の差分
→C#: 出典を追加。 |
|||
36行目: | 36行目: | ||
このイテレータは、ジェネレータの定義中の各yield文の所まで実行した状態を保存する[[コールスタック|スタックフレーム]]を保持するオブジェクトであると考えることができる。イテレータの<code>next()</code>が呼び出されると、Pythonは保存されたフレームを復帰し、次のyield文に到達するまで実行する。yield文の実行によりフレームは再び保存され、yieldの引数の値が<code>next()</code>の呼び出し元に返される。 |
このイテレータは、ジェネレータの定義中の各yield文の所まで実行した状態を保存する[[コールスタック|スタックフレーム]]を保持するオブジェクトであると考えることができる。イテレータの<code>next()</code>が呼び出されると、Pythonは保存されたフレームを復帰し、次のyield文に到達するまで実行する。yield文の実行によりフレームは再び保存され、yieldの引数の値が<code>next()</code>の呼び出し元に返される。 |
||
< |
<syntaxhighlight lang="python"> |
||
def countfrom(n): |
def countfrom(n): |
||
while True: |
while True: |
||
70行目: | 70行目: | ||
>>> f.next() |
>>> f.next() |
||
7 |
7 |
||
</syntaxhighlight> |
|||
</source> |
|||
上記の例は Python 2.5 以上か、[[NumPy]] モジュールの any() 関数を使用できる環境で動作する。 |
上記の例は Python 2.5 以上か、[[NumPy]] モジュールの any() 関数を使用できる環境で動作する。 |
||
95行目: | 95行目: | ||
* <code>yield break</code>は値の生成を終了する |
* <code>yield break</code>は値の生成を終了する |
||
< |
<syntaxhighlight lang="csharp"> |
||
// startからendまでの整数を生成するジェネレータ |
// startからendまでの整数を生成するジェネレータ |
||
static IEnumerable<int> CountTo(int start, int end) { |
static IEnumerable<int> CountTo(int start, int end) { |
||
126行目: | 126行目: | ||
Console.WriteLine(primes.Current); |
Console.WriteLine(primes.Current); |
||
} |
} |
||
</ |
</syntaxhighlight><!-- プリミティブ型まで var で型推論すると分かりづらくなるので、あえて型を明記している。 --> |
||
== 参考文献 == |
== 参考文献 == |
2020年7月5日 (日) 22:57時点における版
ジェネレータは、プログラムにおいて、数列の各要素の値などを次々と生成(ジェネレート)し他の手続きに渡す、という機能を持っている手続きである。値を渡す方法としては、コールバックのようにして他の手続きを呼ぶものもあれば、呼び出される度に次々と異なる値を返す関数であることもある。
性質
「呼び出される度に次々と異なる値を返す関数」である場合は、参照透過ではない。イテレータは、コンテナに含まれる値ひとつひとつに対して走るジェネレータの一種である。ジェネレータの実装としてはコルーチンやcall/ccやマルチスレッドを使う方法が考えられる。また、言語によって詳細が異なるものを「ジェネレータ」と呼んでいる。擬似乱数発生器は、ジェネレータの一例である。
なおyieldというキーワードを使っていればジェネレータ、と取られることもあるが間違いである。
歴史
CLU(1975年初出)の歴史を記した "A History of CLU" には、「Iterators were inspired by a construct in Alphard called a "generator"」((CLUの)イテレータはAlphardのジェネレータと呼ばれる構成要素に影響を受けた)とある[1]。AlphardのジェネレータはIPL-Vに由来する。IPL-Vにおけるジェネレータは、関数プログラミングにおける代表的な高階関数のひとつであるmap関数に似た働きをするもので、リストの各要素に適用するための手続きと、リストを受け取って、各要素にその手続きを適用したリストを生成する。
他に、Icon・Python[2]・JavaScript[3]にジェネレータと呼ばれるものがある(イテレータも参照)。
例
Python
Pythonでは、関数定義の中にyield文があると、その関数定義は通常の関数を定義するのではなく、一種のコルーチンの記述のようになる。yield文を含む関数は、イテレータと同じインタフェースを持つ呼び出し可能オブジェクトを返す関数になる。ジェネレータの語は、「yield文を含む関数定義により定義された関数」と、それが返す「イテレータと同じインタフェースを持つ呼び出し可能オブジェクト」を、はっきりと区別せずに使われているが、ここでは、前者をジェネレータ、後者をイテレータと呼ぶ。
このイテレータは、ジェネレータの定義中の各yield文の所まで実行した状態を保存するスタックフレームを保持するオブジェクトであると考えることができる。イテレータのnext()
が呼び出されると、Pythonは保存されたフレームを復帰し、次のyield文に到達するまで実行する。yield文の実行によりフレームは再び保存され、yieldの引数の値がnext()
の呼び出し元に返される。
def countfrom(n):
while True:
yield n
n += 1
# Example use: 10 から 20 までの整数を表示する。
for i in countfrom(10):
if i <= 20:
print i
else:
break
# もう一つのジェネレータ。必要に応じて素数をいくらでも作成する
def primes():
n = 2
p = []
while True:
if not any( n % f == 0 for f in p ):
yield n
p.append( n )
n += 1
>>> f = primes()
>>> f.next()
2
>>> f.next()
3
>>> f.next()
5
>>> f.next()
7
上記の例は Python 2.5 以上か、NumPy モジュールの any() 関数を使用できる環境で動作する。
Scheme
Schemeにおいて、継続を使って実装したサンプルがある[4]。
C#
C#にもyieldキーワードがある。
yieldステートメントを含むブロックを反復子 (iterator) もしくは反復子ブロック (iterator block) と呼ぶ[5][6]が、これはジェネレータそのものである。
- 反復子メソッドの戻り値には
IEnumerable
/IEnumerator
もしくはジェネリック版のIEnumerable<T>
/IEnumerator<T>
のいずれかを指定する yield return
は値を生成するyield break
は値の生成を終了する
// startからendまでの整数を生成するジェネレータ
static IEnumerable<int> CountTo(int start, int end) {
for (int i = start; i <= end; ++i) {
yield return i;
}
yield break;
}
// 上記ジェネレータを使用して、10から20までの整数を表示
foreach (int i in CountTo(10, 20)) {
Console.WriteLine(i);
}
// 素数を必要なだけ生成するジェネレータ
static IEnumerator<long> Primes() {
long n = 2L;
var p = new List<long>();
while (true) {
if (!p.Any(x => n % x == 0)) {
yield return n;
p.Add(n);
}
n++;
}
}
// 上記ジェネレータを使用して、1000以下の素数を表示
var primes = Primes();
while (primes.MoveNext()) {
if (1000 < primes.Current) { break; }
Console.WriteLine(primes.Current);
}
参考文献
- ^ Liskov, Barbara (1992年4月). “A History of CLU” (pdf). 2008年3月8日閲覧。
- ^ Python Enhancement Proposals PEP 255: Simple Generators, PEP 289: Generator Expressions, PEP 342: Coroutines via Enhanced Generators
- ^ “New In JavaScript 1.7”. 2006年10月10日閲覧。
- ^ Kiselyov, Oleg (2004年1月). “General ways to traverse collections in Scheme”. 2008年3月8日閲覧。
- ^ yield (C# リファレンス)
- ^ yield (C# リファレンス) | Microsoft Docs
- Stephan Murer, Stephen Omohundro, David Stoutamire and Clemens Szyperski: Iteration abstraction in Sather. ACM Transactions on Programming Languages and Systems, 18(1):1-15 (1996) [1]