例えば素因数分解を与えて約数を全て列挙するコードを書きます。こんな感じでしょうか。
def pows(n, e): yield 1 q = 1 for _ in xrange(e): q *= n yield q def divisors(fs): if not fs: yield 1 else: p, e = fs[0] qs = pows(p, e) for n in divisors(fs[1:]): for q in qs: yield n * q
10^6までの約数の個数を数えると、Pythonで3.4秒、PyPyで2.1秒でした。PyPyはジェネレータが遅いので、リストにしたほうが速くなります。
def pows2(n, e): qs = [ 1 ] q = 1 for _ in xrange(e): q *= n qs.append(q) return qs def divisors2(fs): if not fs: return [ 1 ] else: p, e = fs[0] qs = list(pows2(p, e)) return [ n * q for n in divisors(fs[1:]) for q in qs ]
Pythonで4.5秒、PyPyで1.6秒になりました。
まとめると、
Python | PyPy | |
ジェネレータ | 3.4秒 | 2.1秒 |
リスト | 4.5秒 | 1.6秒 |
【結論】
ジェネレータを使うと美しく書けます。また、長い列を生成して途中で切りたいときもあるでしょう。しかし、上の例のように短くて全部なめることもよくあります。こういうときはスピードアップのために、ジェネレータをやめてリストにすることも考えましょう。