2012年6月8日金曜日

初めてのPython20章

この章リストやforループの応用について書かれてあるようだ。14章の続きみたいなものらしい。

例えば19章のmap関数を使わなくてもリストの表現で次のように掛ける。
>>> [x+10 for x in [0, 1, 2, 3]]
[10, 11, 12, 13]
更にif文を加えるとfilter関数のようになる。
>>> [x for x in range(-5, 5) if x > 0]
[1, 2, 3, 4]
このfor文は何個も続けて(nest)させることができる。
下の2つは別表現で同じことをやっている。
>>> [x * y for x in [0, 1, 2] for y in [3, 4, 5]]
[0, 0, 0, 3, 4, 5, 6, 8, 10]

>>> l = []
>>> for x in [0, 1, 2]:
...     for y in [3, 4, 5]:
...         l.append(x*y)
... 
>>> l
[0, 0, 0, 3, 4, 5, 6, 8, 10]
ちなみに本文によると、これらのリスト表現はmapなどより若干早いとのこと。


続いてはgeneratorと言うものについて。
関数ではreturnで戻り値を返すわけだが、yieldで返すこともできる。
yieldで値を返したときには、関数がその状態を保持してくれているみたいだ。
次の状態に遷移させるにはnext()を使う。具体的には以下のようになる。
>>> def gen(N):
...     for i in range(N):
...         yield i*2
...         
>>> x = gen(3)
>>> x.next()
0
>>> x.next()
2
>>> x.next()
4
>>> x.next()
Traceback (most recent call last):
  File "", line 1, in 
StopIteration
その都度値を生成するので、大きなリストを作るときに役立つのだろう。


yieldは式なので、=の右辺に持ってきたりもできる。
するともう少し高度な使い方が出来るとのこと。
そこで登場するのがsend()メソッド。
generatorとのコミュニケーションを行うことができるのだ。
なんとか理解できたけど、どんな使い方があるのだろうか...
例を示すとこうなる。
>>> def gen():
...     for i in range(5):
...         x = yield i*2
...         print "yes", x
... 
>>> g = gen()
>>> g.next() #yieldまでgeneratorを進める。ここで一度yieldを呼ばずsend()をするとエラーになる。
0
>>> g.next() #next()やsend()をすると止まっていたgeneratorが動くき次のyeildまで進む。
yes None
2
>>> g.send(100) #send()でxに値を与えることができる。
yes 100
4
>>> g.next()
yes None
6
>>> 

上のgeneratorはgenerator関数。 generator式(expression)というのもある。
これらgeneratorは1つのオブジェクトであり、
何個も作って途中で止めておいたり、なんてできない。
>>> g = (c*2 for c in "apple") #generator expression
>>> i = iter(g)                #イタレータを登録
>>> i is g
True
>>> i.next()
'aa'
>>> g.next()
'pp'
>>> i.next()
'pp'
>>> i.next()
'll'
>>> g is i                     #同じオブジェクト
True
>>> 

続く…

0 件のコメント:

コメントを投稿