リストの反復中に要素を削除することは許可されていませんが、反復中にpythonリストに要素を追加することは許可されています。以下に例を示します。
for a in myarr:
if somecond(a):
myarr.append(newObj())
私は自分のコードでこれを試してみましたが、うまくいくようですが、それは私が幸運であり、将来のある時点で壊れるのかどうかわかりませんか?
編集:「myarr」は巨大なので、リストをコピーしたくないので、遅すぎるでしょう。また、「somecond()」で追加されたオブジェクトを確認する必要があります。
編集:ある時点で「somecond(a)」はfalseになるため、無限ループは発生しません。
編集:誰かが「somecond()」関数について尋ねました。 myarrの各オブジェクトにはサイズがあり、「somecond(a)」がtrueで新しいオブジェクトがリストに追加されるたびに、新しいオブジェクトのサイズはaより小さくなります。 「somecond()」には、オブジェクトがどれだけ小さいかを表すイプシロンがあり、小さすぎる場合は「false」を返します
Itertoolsのislice
を使用して、リストの小さい部分にイテレーターを作成できます。その後、繰り返し処理するアイテムに影響を与えることなく、リストにエントリを追加できます。
islice( myarr, 0, len(myarr)-1 )
さらに良いことに、すべての要素を繰り返し処理する必要さえありません。ステップサイズを増分できます。
なぜあなたはそれを慣用的なCの方法でやってみませんか?これは防弾となるはずですが、高速ではありません。 Pythonはリンクリストをたどるので、リストへのインデックス付けはかなり確実です。したがって、これは「画家のシュレミエル」アルゴリズムです。しかし、コードの特定のセクションは実際に問題であり、最初に動作させ、次に必要に応じて高速にすることを心配します。
すべての要素を反復処理する場合:
i = 0
while i < len(some_list):
more_elements = do_something_with(some_list[i])
some_list.extend(more_elements)
i += 1
もともとリストにあった要素だけを繰り返したい場合:
i = 0
original_len = len(some_list)
while i < original_len:
more_elements = do_something_with(some_list[i])
some_list.extend(more_elements)
i += 1
まあ、 http://docs.python.org/tutorial/controlflow.html
ループ内で繰り返されるシーケンスを変更することは安全ではありません(これは、リストなどの可変シーケンスタイプに対してのみ発生します)。繰り返し処理するリストを変更する必要がある場合(たとえば、選択したアイテムを複製するため)、コピーを繰り返し処理する必要があります。
あなたはこれを行うことができます。
bonus_rows = []
for a in myarr:
if somecond(a):
bonus_rows.append(newObj())
myarr.extend( bonus_rows )
要するに:すべての新しいオブジェクトがsomecond()
チェックに失敗することを完全に確信している場合、コードは正常に動作しますが、新しく追加されたオブジェクトを繰り返します。
適切な答えを出す前に、繰り返しながらリスト/辞書を変更するのが悪い考えだと考える理由を理解する必要があります。 for
ステートメントを使用する場合、Python
はcleverになり、毎回動的に計算されたアイテムを返します。 list
を例にとると、python
はインデックスを記憶し、毎回l[index]
を返します。 l
を変更する場合、結果l[index]
は乱雑になる可能性があります。
[〜#〜] note [〜#〜]:これを示すために stackoverflow question があります。
反復中に要素を追加する最悪のケースは、無限ループ、python REPL:
import random
l = [0]
for item in l:
l.append(random.randint(1, 1000))
print item
メモリが使い果たされるか、システム/ユーザーによって強制終了されるまで、数字を連続して出力します。
内部の理由を理解し、解決策を議論しましょう。以下にいくつかを示します。
Originリストを繰り返し、コピーしたリストを変更します。
result = l[:]
for item in l:
if somecond(item):
result.append(Obj())
Pythonへの制御を処理する代わりに、リストを反復する方法を決定します。
length = len(l)
for index in range(length):
if somecond(l[index]):
l.append(Obj())
反復する前に、リストの長さを計算し、length
回だけループします。
Originリストを変更する代わりに、新しいオブジェクトを新しいリストに保存し、後でそれらを連結します。
added = [Obj() for item in l if somecond(item)]
l.extend(added)
Iによってリスト要素に直接アクセスします。次に、リストに追加できます。
for i in xrange(len(myarr)):
if somecond(a[i]):
myarr.append(newObj())
元のリストのコピーを作成し、それを繰り返し処理します。以下の変更されたコードを参照してください
for a in myarr[:]:
if somecond(a):
myarr.append(newObj())
今日も同様の問題がありました。チェックが必要なアイテムのリストがありました。オブジェクトがチェックに合格した場合、結果リストに追加されました。それらがdid n't合格の場合、それらを少し変更し、それらがまだ機能する可能性がある場合(変更後のサイズ> 0)、再確認のためにリストの最後に追加します。
私は次のような解決策に行きました
items = [...what I want to check...]
result = []
while items:
recheck_items = []
for item in items:
if check(item):
result.append(item)
else:
item = change(item) # Note that this always lowers the integer size(),
# so no danger of an infinite loop
if item.size() > 0:
recheck_items.append(item)
items = recheck_items # Let the loop restart with these, if any
私のリストは事実上キューですが、おそらく何らかのキューを使用しているはずです。しかし、私のリストは小さく(10個のアイテムなど)、これも機能します。
ループ中にリストに追加された要素もループでループさせたい場合は、forループの代わりにインデックスとwhileループを使用できます。
i = 0
while i < len(myarr):
a = myarr[i];
i = i + 1;
if somecond(a):
myarr.append(newObj())
1行の代替ソリューション:
reduce(lambda x,y : x +[newObj] if somecond else x,myarr,myarr)
S.Lottの回答を拡張して、新しいアイテムも処理されるようにします。
todo = myarr
done = []
while todo:
added = []
for a in todo:
if somecond(a):
added.append(newObj())
done.extend(todo)
todo = added
最後のリストはdone
にあります。