Archive for 1月 17th, 2011

ジェネレータ式

1月 17th, 2011 by admin

ジェネレータ式とは、ジェネレータを作るための式。リスト内包記法の両端の括弧を[]から()に置き換えると、ジェネレータ式になる。ジェネレータ式で生成したジェネレータはリストと違い1度しか使用できない。(破壊的な動作をする)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
>>> gen = (x**2 for x in range(10))
>>> for i in gen: #リストと同様にループ対象として指定できる。
...   i
...
0
1
4
9
16
25
36
49
64
81
>>> for i in gen: #1度使い終わったジェネレータは、再利用できない。
...   i
...

ジェネレータの利点と欠点

利点

  • メモリ使用量が少ない
    必要に応じて要素を1つずつ生成するのでメモリを節約できる。
  • 動作が高速
    必要に応じて要素を1つずつ生成するので、ジェネレータの生成は、リスト生成と比べて高速である。

欠点

  • 一度しか使えない
    値は1回しか取り出せない。
  • ランダムアクセスできない
    先頭要素からの順にしか値を取り出せない。

ジェネレータ(Generator)使ってみた

1月 17th, 2011 by admin

先ほどの記事で、フィボナッチ数列を生成するイテレータを定義したが、よく考えればジェネレータで十分だった。

ジェネレータでフィボナッチ数列

ジェネレータでは、生成した値を返すときに、returnではなくyieldを使う。yieldを使うと実行権は呼び出し元に戻るが、次にnext()が実行されると実行の流れはジェネレータ内に戻される。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
>>> def fib_gen():
...   i, j = 0, 1
...   while True:
...     i, j = j, i + j
...     yield i
...
>>> fg = fib_gen()
>>> fg.next()
1
>>> fg.next()
1
>>> fg.next()
2
>>> fg.next()
3
>>> fg.next()
5
>>> for i in range(10):
...   fg.next()
...
8
13
21
34
55
89
144
233
377
610
>>>

まとめ

ジェネレータを使うと、イテレータを使うより簡潔に繰り返しを記述できた。フィボナッチ数列を作るなどといった単純な処理であればジェネレータ式のほうが簡潔でよいと思う。逆に、生成後に、繰り返しの条件を変える必要がある場合は、ジェネレータでは記述しにくい気がする。
※イテレータは、内部変数や、nextメ()そのものを書き換えることができる。

1
2
3
4
5
6
7
>>> i = FibIter(100)
>>> list(i)
[1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89]
>>> i = FibIter(100)
>>> i.i = 10
>>> list(i)
[1, 11, 12, 23, 35, 58, 93]