SimplePrograms - Python Wiki
https://wiki.python.org/moin/SimplePrograms
の 18番目のプログラムは、 20行の素数のふるいです。
import itertools
def iter_primes():
# an iterator of all numbers between 2 and +infinity
numbers = itertools.count(2)
# generate primes forever
while True:
# get the first number from the iterator (always a prime)
prime = next(numbers)
yield prime
# this code iteratively builds up a chain of
# filters...slightly tricky, but ponder it a bit
numbers = filter(prime.__rmod__, numbers)
for p in iter_primes():
if p > 1000:
break
print (p)
15 lines プログラムで使った itertools モジュールのイテレータ count() を使うのですね。
https://docs.python.org/ja/3/library/itertools.html
によれば、
count()、 引数:[start,[step]]、 結果:start,start+step,start+2*step,...
となる、無限イテレータ(Infinite iterators)だそうです。
つまり、itertools.count(2) は 2以上の 1づつ増加する数列を生成するのですね。
そして、この数列は、next()で1つづつ取り出せるわけです。
>>> n = itertools.count(2)
>>> p = next(n)
>>> print(p)
2
>>> p = next(n)
>>> print(p)
3
>>> p = next(n)
>>> print(p)
4
>>>
iter_primes()関数の動きを見てみます。
メインのwhile ループの中で、 yield を無視して動作を調べると、
>>> import itertools
>>> numbers = itertools.count(2)
>>> # ここから while ループ
>>> prime = next(numbers)
>>> numbers = filter(prime.__rmod__, numbers)
>>> print(prime)
2
>>> prime = next(numbers)
>>> numbers = filter(prime.__rmod__, numbers)
>>> print(prime)
3
....................
>>> prime = next(numbers)
>>> numbers = filter(prime.__rmod__, numbers)
>>> print(prime)
31
>>> prime = next(numbers)
>>> numbers = filter(prime.__rmod__, numbers)
>>> print(prime)
37
>>> prime = next(numbers)
>>> numbers = filter(prime.__rmod__, numbers)
>>> print(prime)
41
>>>
....................
このように、毎回素数が得られるわけですね。filter() は numbers からprime で割り切れるものを除いて、新しい numbers を生成するメソッドのようです。
その後、for 文を使って、iter_primes() を1回だけ呼び出して、生成された素数のイテレータオブジェクトから1つづつ取り出して(つまり next() のように動作する?) p に代入し、その素数が1000 を越えるまで print(p) で出力するのがこのプログラムです。
しかし、普通のプログラミング言語だと、このような関数を作り出すのは難しいように思います。それを Python では、 yield 文(式?)を使ってこれを実現するようです。
関数 iter_prime() は、中で yield prime つまり、 yield 文を使っているのでジェネレータ関数になります。
最初にこのジェネレータ関数を呼び出すと、
yield 式まで実行して prime の値を返して処理を「一時停止」し、
次にこの関数の一時停止を解除すると、yield の次を実行して while ループで繰り返して yield 式の処理をして、
また一時停止します、
のようになるらしいです。
つまり、
>>> import itertools
>>>
>>> # 1回目
>>> numbers = itertools.count(2)
>>>
>>> prime = next(numbers)
>>> print(prime)
2
>>>
>>> # 2回目
>>> numbers = filter(prime.__rmod__, numbers)
>>> prime = next(numbers)
>>> print(prime)
3
>>>
>>> # 3回目
>>> numbers = filter(prime.__rmod__, numbers)
>>> prime = next(numbers)
>>> print(prime)
5
>>>
===== 以下省略 ====
となるので、ここで生成される関数iter_primes()は、素数の無限イテレータ・オブジェクトを生成し、 for 文の in で指定され最初に1回だけこの関数が実行され、生成されたジェネレータオブジェクトから次々と素数が p に代入されるみたいです。
これは for文の復習として、以下のリファレンスを参考に説明してみました。
------------------------
for 文は、シーケンス (文字列、タプルまたはリスト) や、その他の反復可能なオブジェクト (iterable object) 内の要素に渡って反復処理を行うために使われます:
for_stmt ::= "for" target_list "in" starred_list ":" suite
["else" ":" suite]
starred_list 式は一度だけ評価され、評価結果として イテラブル オブジェクトを返す必要があります。そのイテラブルから イテレータ が作られます。
------------------------
つまり、
>>> a = iter_primes() # 一回しか関数を呼び出さないことを強調します。
>>> a
<generator object iter_primes at 0xf7027db8>
>>> for p in a:
... if p > 6:
... break
... print(p)
...
2
3
5
>>>
です。 この動きはとても興味深いです。
ちょっと理解が怪しくなっているように感じているので、次回以降に yield や filter() を調べて見たいと思います。
https://wiki.python.org/moin/SimplePrograms
の 18番目のプログラムは、 20行の素数のふるいです。
import itertools
def iter_primes():
# an iterator of all numbers between 2 and +infinity
numbers = itertools.count(2)
# generate primes forever
while True:
# get the first number from the iterator (always a prime)
prime = next(numbers)
yield prime
# this code iteratively builds up a chain of
# filters...slightly tricky, but ponder it a bit
numbers = filter(prime.__rmod__, numbers)
for p in iter_primes():
if p > 1000:
break
print (p)
15 lines プログラムで使った itertools モジュールのイテレータ count() を使うのですね。
https://docs.python.org/ja/3/library/itertools.html
によれば、
count()、 引数:[start,[step]]、 結果:start,start+step,start+2*step,...
となる、無限イテレータ(Infinite iterators)だそうです。
つまり、itertools.count(2) は 2以上の 1づつ増加する数列を生成するのですね。
そして、この数列は、next()で1つづつ取り出せるわけです。
>>> n = itertools.count(2)
>>> p = next(n)
>>> print(p)
2
>>> p = next(n)
>>> print(p)
3
>>> p = next(n)
>>> print(p)
4
>>>
iter_primes()関数の動きを見てみます。
メインのwhile ループの中で、 yield を無視して動作を調べると、
>>> import itertools
>>> numbers = itertools.count(2)
>>> # ここから while ループ
>>> prime = next(numbers)
>>> numbers = filter(prime.__rmod__, numbers)
>>> print(prime)
2
>>> prime = next(numbers)
>>> numbers = filter(prime.__rmod__, numbers)
>>> print(prime)
3
....................
>>> prime = next(numbers)
>>> numbers = filter(prime.__rmod__, numbers)
>>> print(prime)
31
>>> prime = next(numbers)
>>> numbers = filter(prime.__rmod__, numbers)
>>> print(prime)
37
>>> prime = next(numbers)
>>> numbers = filter(prime.__rmod__, numbers)
>>> print(prime)
41
>>>
....................
このように、毎回素数が得られるわけですね。filter() は numbers からprime で割り切れるものを除いて、新しい numbers を生成するメソッドのようです。
その後、for 文を使って、iter_primes() を1回だけ呼び出して、生成された素数のイテレータオブジェクトから1つづつ取り出して(つまり next() のように動作する?) p に代入し、その素数が1000 を越えるまで print(p) で出力するのがこのプログラムです。
しかし、普通のプログラミング言語だと、このような関数を作り出すのは難しいように思います。それを Python では、 yield 文(式?)を使ってこれを実現するようです。
関数 iter_prime() は、中で yield prime つまり、 yield 文を使っているのでジェネレータ関数になります。
最初にこのジェネレータ関数を呼び出すと、
yield 式まで実行して prime の値を返して処理を「一時停止」し、
次にこの関数の一時停止を解除すると、yield の次を実行して while ループで繰り返して yield 式の処理をして、
また一時停止します、
のようになるらしいです。
つまり、
>>> import itertools
>>>
>>> # 1回目
>>> numbers = itertools.count(2)
>>>
>>> prime = next(numbers)
>>> print(prime)
2
>>>
>>> # 2回目
>>> numbers = filter(prime.__rmod__, numbers)
>>> prime = next(numbers)
>>> print(prime)
3
>>>
>>> # 3回目
>>> numbers = filter(prime.__rmod__, numbers)
>>> prime = next(numbers)
>>> print(prime)
5
>>>
===== 以下省略 ====
となるので、ここで生成される関数iter_primes()は、素数の無限イテレータ・オブジェクトを生成し、 for 文の in で指定され最初に1回だけこの関数が実行され、生成されたジェネレータオブジェクトから次々と素数が p に代入されるみたいです。
これは for文の復習として、以下のリファレンスを参考に説明してみました。
------------------------
for 文は、シーケンス (文字列、タプルまたはリスト) や、その他の反復可能なオブジェクト (iterable object) 内の要素に渡って反復処理を行うために使われます:
for_stmt ::= "for" target_list "in" starred_list ":" suite
["else" ":" suite]
starred_list 式は一度だけ評価され、評価結果として イテラブル オブジェクトを返す必要があります。そのイテラブルから イテレータ が作られます。
------------------------
つまり、
>>> a = iter_primes() # 一回しか関数を呼び出さないことを強調します。
>>> a
<generator object iter_primes at 0xf7027db8>
>>> for p in a:
... if p > 6:
... break
... print(p)
...
2
3
5
>>>
です。 この動きはとても興味深いです。
ちょっと理解が怪しくなっているように感じているので、次回以降に yield や filter() を調べて見たいと思います。