2010-12-10 7 views
3

インデックスループよりも速いと聞いて以来、繰り返しのためにイテレータを使用しようとしています。私がよく分からないことの1つは、シーケンスの終わりをきれいに扱う方法です。私が考えることができる方法はtryexcept StopIterationです。これは私には醜いものです。Pythonイテレータをうまく使用する方法

より具体的には、2つのソートされたリストabのマージされたソート済みリストを印刷するように求められたとします。私は次のように書くでしょう

aNull = False 
I = iter(a) 
try: 
    tmp = I.next() 
except StopIteration: 
    aNull = True 

for x in b: 
    if aNull: 
     print x 
    else: 
     if x < tmp: 
      print x 
     else: 
      print tmp,x 
      try: 
       tmp = I.next() 
      except StopIteration: 
       aNull = True 

while not aNull: 
    print tmp 
    try: 
     tmp = I.next() 
    except StopIteration: 
     aNull = True 

どうやってそれを整えるのですか?

+0

「b」とは何ですか? – detly

+2

そのコードはほとんど判読できません。それが何をすべきか説明する。 –

+0

aとbはソートされた2つのリストです。これらの2つのリストの要素を非増加順で印刷することです。 – nos

答えて

7

私は扱いやすいと思うのですが、対称的に読むと分かりやすいでしょう。abまた、デフォルト値ではPython 2.6でビルトインnext機能を使用すると、StopIterationを処理する必要性が回避:

def merge(a, b): 
    """Merges two iterators a and b, returning a single iterator that yields 
    the elements of a and b in non-decreasing order. a and b are assumed to each 
    yield their elements in non-decreasing order.""" 

    done = object() 
    aNext = next(a, done) 
    bNext = next(b, done) 

    while (aNext is not done) or (bNext is not done): 
     if (bNext is done) or ((aNext is not done) and (aNext < bNext)): 
      yield aNext 
      aNext = next(a, done) 
     else: 
      yield bNext 
      bNext = next(b, done) 

for i in merge(iter(a), iter(b)): 
    print i 

次の関数は、任意の数のイテレータのために働くのアプローチを一般化します。

def merge(*iterators): 
    """Merges a collection of iterators, returning a single iterator that yields 
    the elements of the original iterators in non-decreasing order. Each of 
    the original iterators is assumed to yield its elements in non-decreasing 
    order.""" 

    done = object() 
    n = [next(it, done) for it in iterators] 

    while any(v is not done for v in n): 
     v, i = min((v, i) for (i, v) in enumerate(n) if v is not done) 
     yield v 
     n[i] = next(iterators[i], done) 
+1

もちろん、2つのリストをマージしたい場合は、標準ライブラリ関数 'heapq.merge'を使うべきです。 – jchl

+0

これは、ジェネレータとして行われた方が良いでしょう - それにaとbを渡し、 'print'ステートメントを' yield'に置き換えてください。それから、あなたが望むものを何でもでき、イテレータになることができます。 – neil

+0

@neil合意。私はそれを考えましたが、この例では余分な複雑さの価値があるとは考えていませんでした。しかし、あなたもそれを言及して以来、私はあなたが示唆するように私はそれを書き直すと思います。 – jchl

5

イテレータの全体が欠落しています。 I.next()を手動で呼び出すのではなく、Iを反復するだけです。

for tmp in I: 
    print tmp 

itertoolsモジュールに非常に便利な機能を使用し、2回の反復子をマージする

を編集しました。あなたがしたい1はおそらくizipです:

merged = [] 
for x, y in itertools.izip(a, b): 
    if x < y: 
     merged.append(x) 
     merged.append(y) 
    else: 
     merged.append(y) 
     merged.append(x) 

編集再びコメントで指摘したように

、次のより小さいリストから複数の項目があるかもしれませんので、これは実際には、動作しません。リストの項目b。しかし、これに対処する別の組み込みファンクションがあることに気付きました:heapq.merge

+1

'for'を使って2つのイテレーターをマージする方法がわかりません。 – jchl

+0

これはうまくいかないでしょう - 2つのイテレータの間には、複数のアイテムがあるかもしれません。 – neil

+0

@neilはい、私はそれを実現しました。もう少し考える必要があります。 –

0

機能sortedは、リストとイテレータで機能します。たぶんあなたが望むものではないかもしれませんが、次のコードが動作します。

 

a.expand(b) 
print sorted(iter(a)) 
 
+0

iter(a)をリストにソートしてソートし、ソートして、ジェネレータを使用していない場合... – Ant

+0

おかげで、ありがとう – jaume

関連する問題