python3x: generators

generatorは必要とされるメモリ量が比較的少なく且つ他のiteratorと比べて必要なコードのライン数が少ないことが魅力的なポイントの一つ。

#このトピックに関連している投稿
- <a href="http://qiita.com/weedslayer/items/11de0f314ccbb70de812" target="_blank">python3x: returnとyieldの違い</a>
- <a href="http://qiita.com/weedslayer/items/5581cbd1276e0e80a855" target="_blank">python3x: iterables vs iterators</a>
- <a href="http://qiita.com/weedslayer/items/dce623d9f343a55c0123" target="_blank">python3x: generator.send(value)って何？<a/>

#iterator vs generator
iteratorとの違いはgeneratorは`yield`を使ってgeneratorと呼ばれるiteratorを返す（generator iteratorとも呼ぶ）。generator iteratorは`yield`によって返される。

```
def <generator_fn_name>():
    <var> = <value>
    while <predicate>:
        yield <value>
        <increment var>
```

基本的には\_\_iter\_\_と同じ機能を備えている。

>_Calling the \_\_iter\_\_ method makes a generator object return itself without modifying its current state.
Calling the \_\_next\_\_ method makes a generator object compute and return the next object in its sequence. If the sequence is exhausted, StopIteration is raised.
Typically, a generator should not restart unless it's defined that way. But calling the generator function returns a brand new generator object (like calling \_\_iter\_\_ on an iterable object)._

ただgeneratorの場合は

- `__next__`と`__iter__`をわざわざ書かなくてもいい。
- `__iter__`は`return`を使うがgeneratorは`yield`を用いてgenerator iteratorを返す。
- generatorは`__next__`の情報を記録したまま次の`__next__`関数を呼ぶ。

#returnとyield

<a href="http://qiita.com/weedslayer/items/11de0f314ccbb70de812" target="_blank">python3x: returnとyieldの違い<a/>


#generatorの挙動

generatorの基本的な動作は以下のとおり:

一回目の`__next__`call:

>_1. Enter the function, run until the line with yield.
2. Return the value in the yield statement, but remember the state of the function for future \_\_next\_\_ calls._

それ以降の`__next__`call:

>_1. Re-enter the function, start at the line after yield, and run until the next yield statement.
2. Return the value in the yield statement, but remember the state of the function for future \_\_next\_\_ calls._

```
def generator():
    print("Starting here")
    i = 0
    while i < 6:
        print("Before yield")
        yield i
        print("After yield")
        i += 1

>>> g = generator()
>>> g
<generator object generator at 0x106c1e9e8>
>>> g == iter(g)
True
>>> i = iter(g)
>>> i
<generator object generator at 0x106c1e9e8>
>>> next(g)
Starting here
Before yield
0
>>> next(g)
After yield
Before yield
1
```

#例題

```
def foo():
    result = []
    for ... in ...:
        result.append(x)
    return result
```

をこれに書き換えられる:

```
def iter_foo():
    for ... in...:
        yield x
```

#追記:n番目の要素を取り出したい時

`list(itertools.islice(it, 0, n, 1))`もしくは

```
def nth(iterable, n, default=None):
    "Returns the nth item or a default value"
    return next(islice(iterable, n, None), default)
```

で取得可能。

#generatorはどういうときに便利なのか

generatorは実行処理の規模が大きいストリーム処理などの場合（壮大な数を含んだリストなど）に役に立つ。通常の関数だとリストを生成する時点で大量のメモリを食べてしまうので処理自体が遅くなってしまう。そこでgeneratorを使って必要に応じてリストからデータを取り出して処理を行う。`return`を用いた関数はどこまで処理をしていたのかを記録してくれないので`yield`を使う。

#実際にgeneratorを作ってみる

以上を踏まえて<a href="https://projecteuler.net/problem=10" target="_blank">Summation of primes</a>の問題はgeneratorを関数を試してみるきっかけとしていいかもしれない。

>_The sum of the primes below 10 is 2 + 3 + 5 + 7 = 17. Find the sum of all the primes below two million._

#参考にしたリンク

- [Difference between Python's Generators and Iterators](http://stackoverflow.com/questions/2776829/difference-between-pythons-generators-and-iterators)
- [Iterables vs. Iterators vs. Generators](http://nvie.com/posts/iterators-vs-generators/)
- [Get multiple individual values from generator in Python](http://stackoverflow.com/questions/13552839/get-multiple-individual-values-from-generator-in-python)
- [How to get the n next values of a generator in a list (python)](http://stackoverflow.com/questions/4152376/how-to-get-the-n-next-values-of-a-generator-in-a-list-python)
- [Improve Your Python: 'yield' and Generators Explained]()