ジェネレータの使い方

#ジェネレーター

ジェネレータ関数は簡単に言うと、関数の中の途中で`return`ができる感じ。
でも、`return`的なことをしても終わらないのが特徴。
あと、一つづつ返していきのでメモリもあまり使わない。
python3で追加されたサブジェネレータも解説していきます。
今回作ったコード：https://github.com/KodairaTomonori/Qiita/tree/master/default_module/syntax
実際は`return`の代わりに`yield`を使います。
まずは簡単に、ただ単に数を数える`counter`と`fibonatti`数列の生成関数から
##基本的な使い方（yield, .\_\_next__(), .close() )
```counter_and_fibonati.py
def counter():
    num = 0
    while True:
        yield num
        num += 1

def fibonatti():
    now = 0
    next_num = 1
    while True:
        yield now
        now, next_num = next_num, now + next_num

if __name__ == '__main__':
    count = counter()
    print('print 0-3, for i in count')
    for i in count:
        print(i, end=', ')
        if i >= 3:
            print()
            break
    print('next_count, count.__next__()')
    print(count.__next__())
    print('print 5-7, for i in count')
    for i in count:
        print(i, end=', ')
        if i >= 7:
            count.close()
            print()
    print('print fibonatti')
    for i in fibonatti():
       if i > 100: break
       print(i, end=', ')
    print(i)
```
###出力
>print 0-3, for i in count
0, 1, 2, 3, 
next_count, count.\_\_next__()
4
print 5-7, for i in count
5, 6, 7, 
print fibonatti
0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144

###解説
まず、`counter`関数は簡単で、`num=0`からスタートして、`while`の無限ループには入ります。
1ループごとに`yield num`で`num`を返します。
次に、`counter`が呼び出された？ときに`num+1`して、その後`yield num`を返します。
呼び出されるごとに`+1`した値が帰ってきます。
使うときは、`main`のとこに書いてありますが、`for`文に入れると1ループごとに順次、値を`0,1,2,3`を返してくれます。

`count = counter()`と変数として定義することで、その状態(`num`)を保存しておけます。

`count.__next__()`を使うと次の数、つまり`4`が帰ってきます。

次のfor文もさっき同様に1づつ足して返してくれます。
ですが、さっきとは違い、`count`の状態は`num=4`で止まっているのでこのループでは`5`から始まります。
で、最後に`7`以上になったっと気に`count.close()`をして、ループを終了させています。

`.close()`を使うと`for`文を抜け、なおかつ`count`のジェネレータとしての機能がなくなります。
この後に、`count.__next__()`と`StopIteration`吐き出します。

`fibonatti`の方はほぼ一緒なので特に問題ないですね。
こっちでは、`fibonatti()`と直接ジェネレータを置いているので、`for`文を抜けた後`now, next_num`はが保存されているジェネレータはどっかに行ってしまいます。

`for`文で回せるということは`list()`で囲むと`list`に出来ますが、その場合、ジェネレータ関数内に制限を設けないと無限ループして死ぬので注意です。

##呼び出すごとに引数を与えたい時
`.send(x)`で引数をその都度渡すことができます。
`.send(x)`は`yield`の位置に`x`を与えるため、一回`.__next__()`を執行しなければなりません。
###
```generator_send.

def generator(step):
    val = 0
    prev = 0
    while True:
        if step == None:
            step = prev
        prev = step
        val += step
        step = yield val

if __name__ == '__main__':
    gen = generator(0)
    print(gen.__next__(), end=', ')
    for i in [1,2,3,4,5,6]:
        print(gen.send(i) , end=', ')
    print()
```
###出力
>0, 1, 3, 6, 10, 15, 21, 

###解説
`generator`関数は、受け取った引数`step`を`val`に足すプログラムです。
最初に、`generator(0)`初期値を0に設定します。
次に`.__next__()`を使い、`step = yield val`まで行きます。
そうすると、`0`が帰ってきます。
そして次にfor文には入り、`1から6`まで回しいきます。
`gen.send(i)`が実行されると、前回`step = yield val`のところに`i`が入ります。
つまり、`gen.send(i)`をやると`step = i`となります。
そんな感じでループして行きます。ここで、`.send()`を使わず、`.__next__()`をすると`step = None`となります。

#python3の新機能：サブジェネレータ
サブジェネレータは、`yield　from generator`とやると、generatorを実行してくれます。
`generator`はジェネレータ関数です。
これを使うと再帰がとても便利になります。

同じものを含む順列の生成のプログラムをサンプルとして説明します。
##
```ex_permutation.py
def ex_permutation(iterable, now_list=[]):
    if not iterable: 
        yield now_list
        return
    for i in [iterable.index(i) for i in set(iterable) ]:
        yield from permutation(iterable[:i] + iterable[i+1:], now_list + [iterable[i] ])

if __name__ == '__main__':
    permu = ex_permutation([2,2,3])
    for i in permu:
        print(i)
```
###出力
>[2, 2, 3]
[2, 3, 2]
[3, 2, 2]

##解説
中身はどうでもいいんですが、`set()`をつかってかぶんないように再帰的にやってるだけです。
で、`yield from`で再帰的に書くことができます。
最後に`iterable`の中身がなくなった時に`now_list`を返してくれます。
なんかわかんないけど便利！
これがないとややこしいことになるらしい。

#まとめ
`yield`で関数の中身の途中で値を返せる。
`.close()`でジェネレータ関数を終了できる。
`.__next__()`で次を実行する
`.send()`で値を渡せる
`yield from`は便利。