バリケンのPython日記 RSSフィード

2008-12-17

[] イテレータとジェネレータ(5)  イテレータとジェネレータ(5) - バリケンのPython日記 を含むブックマーク はてなブックマーク -  イテレータとジェネレータ(5) - バリケンのPython日記  イテレータとジェネレータ(5) - バリケンのPython日記 のブックマークコメント

この間の続きだよ。

前回の例で、

  • 関数定義中にひとつでもyield文を書くと、その関数はジェネレータ関数になる
  • ジェネレータ関数を評価するとジェネレータが返る
  • そのジェネレータに対してnext()メソッドを実行すると関数の実行が始まり、yield文のところで関数の呼び出し元に返る

ということがわかったよ。

前回の例ではyield文のあとに何も書いていなかったけど、yield文に続いて式を記述しておくと、next()メソッドの呼び出し元にその評価結果を返すことが出来るよ。というか、ジェネレータは元々「値の生成器」だから値を返すのは当然だよね。

じゃあ、値が返るジェネレータの例だよ。next()メソッドを呼ぶたびにyield文が逐次実行されていき、戻り値が適宜返ってきていることが分かるよ。

>>> def fire():
	yield 3
	yield 2
	yield 1
	yield 'Fire!'

	
>>> g = fire()
>>> g.next()
3
>>> g.next()
2
>>> g.next()
1
>>> g.next()
'Fire!'
>>> g.next()

Traceback (most recent call last):
  File "<pyshell#11>", line 1, in <module>
    g.next()
StopIteration
>>> 

こんな感じでyield文をいくつも書くのは大変だから、通常のジェネレータ関数の場合はループの中にyield文を書くよ。たとえばフィボナッチ数列を生成するジェネレータ関数は次のようになるよ。

>>> def fibo(n):
	i, j = 0, 1
	for x in range(n):
		yield i
		i, j = j, i + j


>>> g = fibo(10)
>>> g.next()
0
>>> g.next()
1
>>> g.next()
1
>>> g.next()
2
>>> g.next()
3
>>> g.next()
5
>>> g.next()
8
>>> g.next()
13
>>> g.next()
21
>>> g.next()
34
>>> g.next()

Traceback (most recent call last):
  File "<pyshell#93>", line 1, in <module>
    g.next()
StopIteration
>>> 

ついでに、みんな大好きFizzBuzz問題をジェネレータを使って解いてみるよ。

>>> def fizzbuzz(n):
	for i in range(1, n + 1):
		if i % 15 == 0:
			yield "FizzBuzz"
		elif i % 5 == 0:
			yield "Buzz"
		elif i % 3 == 0:
			yield "Fizz"
		else:
			yield i

			
>>> for x in fizzbuzz(100):
	print x
(実行結果省略)
トラックバック - http://python.g.hatena.ne.jp/muscovyduck/20081217