2017-02-08 13 views
0

ジェネレータの次の要素を返す関数を作成しようとしています。ジェネレータの最後にある場合は、それをリセットして次の結果を返します。以下のコードの出力は、次のようになります。Pythonでジェネレータをリセットするための再帰

1 
2 
3 
1 
2 

ただし、これは明らかにわかりません。私は何をしているのでしょうか?

a = '123' 

def convert_to_generator(iterable): 
    return (x for x in iterable) 

ag = convert_to_generator(a) 

def get_next_item(gen, original): 
    try: 
     return next(gen) 
    except StopIteration: 
     gen = convert_to_generator(original) 
     get_next_item(gen, original) 

for n in range(5): 
     print(get_next_item(ag,a)) 

1 
2 
3 
None 
None 
+1

'convert_to_generator'とは何ですか?そのコードも投稿してください。 – Jarvis

+2

['itertools.cycle'](https://docs.python.org/2/library/itertools.html#itertools.cycle)に興味があるかもしれません。 –

+0

私はそれを@Jarvis – mnky9800n

答えて

0

あなたは、再帰呼び出しの結果を返す必要があります。まだこの作業アプローチをしない

return get_next_item(gen, original) 

を。 forループで使用されている生成器agは、関数内のローカル変数genの再バインドによって変更されません。それは消耗したままになります...

コメントに記載されているように、itertools.cycleをチェックしてください。

+0

私はこれをすでに実際に試してみました。最初の要素 '1,2,3,1,1'だけを返し始めます。 – mnky9800n

+1

'return'が' StopIteration'をスローするので、これはうまくいかないでしょう... – CodenameLambda

0

get_next_itemは、__next__メソッドで値yieldを返すイテレータを返すジェネレータです。そのため、あなたの声明は何もしません。

何がやりたいことはこれです:

def get_next_item(gen, original): 
    try: 
     return next(gen) 
    except StopIteration: 
     gen = convert_to_generator(original) 
     for i in get_next_item(gen, original): 
      return i 

か短い、と完全に同等の(限りgenが、それはおそらく持っている__iter__方法を、持っているとして):

def get_next_item(gen, original): 
    for i in gen: 
     yield i 
    for i in get_next_item(convert_to_generator(original)): 
     yield i 

または再帰なし(これはPythonの大きな問題です。深さに制限があり、遅いです):

def get_next_item(gen, original): 
    for i in gen: 
     yield i 
    while True: 
     for i in convert_to_generator(original): 
      yield i 

convert_to_generatorがiterにだけ呼び出している場合は、それも短いです:

def get_next_item(gen, original): 
    for i in gen: 
     yield i 
    while True: 
     for i in original: 
      yield i 

または、itertoolsと:

import itertools 

def get_next_item(gen, original): 
    return itertools.chain(gen, itertools.cycle(original)) 

get_next_itemgenをすることが保証されている場合itertools.cycleに相当しますoriginalのイテレータです。

サイドノート:yield from xxは式です)のPython 3.3以降でfor i in x: yield iを交換できます。

+0

'original'が空のシーケンスの場合、これは無限ループになります。 – Copperfield

+0

@Copperfieldどうしたら違うのですか?反復を止める?例外を投げる?または、最後の要素を繰り返しますか?私のコードスニペットは、mnky9800nのコードとまったく同じです。編集:彼は何を_彼のコードを実行する必要があります。 – CodenameLambda

+0

単純な 'while True'の代わりに' original while'を実行すると、 'original'が空であれば何も無限ループに入りません。 – Copperfield

0

簡単な方法はitertools.cycleを使用してください。それ以外の場合は、iterableがイテレータ(別名ジェネレータ)であれば、iterableの要素を覚えておく必要があります。何度でもそれを再使用してください。

ドキュメントの実装例を含め

def cycle(iterable): 
    # cycle('ABCD') --> A B C D A B C D A B C D ... 
    saved = [] 
    for element in iterable: 
     yield element 
     saved.append(element) 
    while saved: 
     for element in saved: 
      yield element 

または例えば、再利用のことを行うために

def cycle(iterable): 
    # cycle('ABCD') --> A B C D A B C D A B C D ... 
    if iter(iterable) is iter(iterable): # is a iterator 
     saved = [] 
     for element in iterable: 
      yield element 
      saved.append(element) 
    else: 
     saved = iterable 
    while saved: 
     for element in saved: 
      yield element 

たとえば、あなたのコードについて今

test = cycle("123") 
for i in range(5): 
    print(next(test))  

を使用し、問題がありますシンプル、それはそれを覚えていない状態

新しいジェネレータが構築されてマークされた行の
def get_next_item(gen, original): 
    try: 
     return next(gen) 
    except StopIteration: 
     gen = convert_to_generator(original) # <-- the problem is here 
     get_next_item(gen, original)   #and you should return something here 

210、しかし、あなたは、要素を返すために、あなたの関数を変更するように、それを行うための方法がある欲求の行動を取得するには、この関数の外であなたのag変数を更新する必要があるでしょうし、ジェネレータには他の方法もありますが、クラスを作成してその状態を覚えておくように推奨されたり複雑になったりすることはありません。