Pythonでタプルのリストを繰り返し処理しており、特定の基準を満たしている場合はそれらを削除しようとしています。
for tup in somelist:
if determine(tup):
code_to_remove_tup
代わりに何を使うべきcode_to_remove_tup
ですか?この方法でアイテムを削除する方法がわかりません。
リスト内包表記を使用して、削除したくない要素のみを含む新しいリストを作成できます。
somelist = [x for x in somelist if not determine(x)]
または、スライスsomelist[:]
に割り当てることで、既存のリストを変更して、必要なアイテムのみを含めることができます。
somelist[:] = [x for x in somelist if not determine(x)]
somelist
このアプローチは、変更を反映する必要のある他の参照がある場合に役立つ可能性があります。
理解の代わりに、を使用することもできますitertools
。Python 2の場合:
from itertools import ifilterfalse
somelist[:] = ifilterfalse(determine, somelist)
またはPython3:
from itertools import filterfalse
somelist[:] = filterfalse(determine, somelist)
リスト内包表記を示唆する答えはほとんど正しいです-完全に新しいリストを作成し、古いリストと同じ名前を付けることを除いて、古いリストを変更しません。これは、 @ Lennartの提案のように、選択的に削除する場合とは異なります。高速ですが、複数の参照を介してリストにアクセスする場合は、参照の1つを再配置するだけで、リストオブジェクトを変更しないという事実があります。それ自体が微妙で悲惨なバグにつながる可能性があります。
幸いなことに、リスト内包表記の速度と、インプレース変更に必要なセマンティクスの両方を取得するのは非常に簡単です。コードだけです。
somelist[:] = [tup for tup in somelist if determine(tup)]
他の回答との微妙な違いに注意してください:これはベアネームに割り当てられていません-たまたまリスト全体であるリストスライスに割り当てられているため、1つの参照を再配置するのではなく、同じPythonリストオブジェクト内のリストコンテンツ を置き換えます(前のリストオブジェクトから新しいリストオブジェクトへ)他の答えのように。
リストのコピーを取り、最初にそれを反復処理する必要があります。そうしないと、予期しない結果になる可能性がある反復が失敗します。
例(リストの種類によって異なります):
for tup in somelist[:]:
etc....
例:
>>> somelist = range(10)
>>> for x in somelist:
... somelist.remove(x)
>>> somelist
[1, 3, 5, 7, 9]
>>> somelist = range(10)
>>> for x in somelist[:]:
... somelist.remove(x)
>>> somelist
[]
for i in range(len(somelist) - 1, -1, -1):
if some_condition(somelist, i):
del somelist[i]
あなたは後ろに行く必要があります、さもなければそれはあなたが座っている木の枝をのこぎりで切るようなものです:-)
Python 2ユーザー:ハードコードされたリストの作成を回避するためにrange
置き換えますxrange
回避策の概要
また:
リンクリストの実装を使用する/独自のロールを作成します。
リンクリストは、効率的なアイテムの削除をサポートするための適切なデータ構造であり、空間と時間のトレードオフを強制するものではありません。
CPythonは、ここで説明したように動的配列list
で実装されますが、これは削除をサポートするのに適したデータ型ではありません。
ただし、標準ライブラリにはリンクリストがないようです。
新しいlist()
ものを最初から始めて、 https.append()
://stackoverflow.com/a/1207460/895245で述べられているように最後に戻ってください
今回は効率的ですが、反復中に配列の余分なコピーを保持するため、スペース効率は低くなります。
https://stackoverflow.com/a/1207485/895245del
で言及されているようにインデックスで使用する
これは、配列のコピーをディスペンスするため、スペース効率が高くなりますが、動的配列から削除するには、後続のすべての項目を1つ戻す(O(N))必要があるため、時間効率が低くなります。
一般に、それをすばやく汚くしていて、カスタムLinkedList
クラスを追加したく.append()
ない場合は、メモリが大きな問題でない限り、デフォルトでより高速なオプションを選択するだけです。
公式Python2チュートリアル4.2。「ステートメント用」
https://docs.python.org/2/tutorial/controlflow.html#for-statements
ドキュメントのこの部分は、次のことを明確にしています。
[:]
ループ内で繰り返し処理するシーケンスを変更する必要がある場合(たとえば、選択したアイテムを複製する場合)、最初にコピーを作成することをお勧めします。シーケンスを反復処理しても、暗黙的にコピーは作成されません。スライス表記はこれを特に便利にします:
>>> words = ['cat', 'window', 'defenestrate'] >>> for w in words[:]: # Loop over a slice copy of the entire list. ... if len(w) > 6: ... words.insert(0, w) ... >>> words ['defenestrate', 'cat', 'window', 'defenestrate']
Python2のドキュメント7.3。「forステートメント」
https://docs.python.org/2/reference/compound_stmts.html#for
ドキュメントのこの部分では、コピーを作成する必要があることをもう一度説明し、実際の削除の例を示しています。
注:シーケンスがループによって変更されている場合は微妙です(これは、可変シーケンス、つまりリストでのみ発生する可能性があります)。内部カウンターは、次に使用されるアイテムを追跡するために使用され、これは反復ごとに増分されます。このカウンターがシーケンスの長さに達すると、ループは終了します。これは、スイートがシーケンスから現在の(または前の)アイテムを削除した場合、次のアイテムはスキップされることを意味します(すでに処理された現在のアイテムのインデックスを取得するため)。同様に、スイートが現在のアイテムの前にシーケンス内のアイテムを挿入した場合、現在のアイテムは次回ループを介して再度処理されます。これは厄介なバグにつながる可能性があり、シーケンス全体のスライスを使用して一時的なコピーを作成することで回避できます。
for x in a[:]:
if x < 0: a.remove(x)
ただし、値を見つけるためにリスト全体.remove()
を反復処理する必要があるため、この実装には同意しません。
Pythonはこれをより良くすることができますか?
この特定のPythonAPIは改善できるようです。たとえば、次のように比較します。
std::vector::erase
要素が削除された後、要素に有効なイテレータを返すC ++どちらも、イテレータ自体を除いて、反復されるリストを変更できないことを明確にし、リストをコピーせずに変更するための効率的な方法を提供します。
ArrayList
おそらく根本的な理論的根拠は、Pythonリストは動的配列に裏打ちされていると想定されているため、Javaはとの両方のLinkedList
実装を備えたより優れたインターフェイス階層を備えているのに対し、どのタイプの削除も時間効率が悪いということですListIterator
。
Pythonstdlibにも明示的なリンクリストタイプがないようです。Pythonリンクリスト
このような例に対する最善のアプローチは、リスト内包表記です。
somelist = [tup for tup in somelist if determine(tup)]
関数を呼び出すよりも複雑なことをしている場合はdetermine
、新しいリストを作成して、リストに追加することをお勧めします。例えば
newlist = []
for tup in somelist:
# lots of code here, possibly setting things up for calling determine
if determine(tup):
newlist.append(tup)
somelist = newlist
を使用してリストをコピーするremove
と、以下の回答の1つで説明されているように、コードが少しきれいに見える場合があります。O(n)
remove
これは、最初にリスト全体をコピーし、削除される各要素に対して操作を実行して、これをO(n^2)
アルゴリズムにするため、非常に大きなリストに対しては絶対にこれを行わないでください。
for tup in somelist[:]:
# lots of code here, possibly setting things up for calling determine
if determine(tup):
newlist.append(tup)
関数型プログラミングが好きな人のために:
somelist[:] = filter(lambda tup: not determine(tup), somelist)
また
from itertools import ifilterfalse
somelist[:] = list(ifilterfalse(determine, somelist))
私はこれを巨大なリストで行う必要があり、リストを複製することは費用がかかるように思われました。特に私の場合、残っているアイテムと比較して削除の数が少ないためです。私はこの低レベルのアプローチを採用しました。
array = [lots of stuff]
arraySize = len(array)
i = 0
while i < arraySize:
if someTest(array[i]):
del array[i]
arraySize -= 1
else:
i += 1
私が知らないのは、大きなリストをコピーするのに比べて、いくつかの削除がどれほど効率的かということです。何か洞察があればコメントしてください。
現在のリスト項目が目的の基準を満たしている場合は、新しいリストを作成するのも賢明かもしれません。
それで:
for item in originalList:
if (item != badValue):
newList.append(item)
プロジェクト全体を新しいリスト名で再コーディングする必要がないようにするには、次のようにします。
originalList[:] = newList
注、Pythonドキュメントから:
copy.copy(x)xの浅いコピーを返します。
copy.deepcopy(x)xのディープコピーを返します。
ここでの回答のほとんどは、リストのコピーを作成することを求めています。リストが非常に長い(110Kアイテム)というユースケースがあり、代わりにリストを減らし続ける方が賢明でした。
まず、foreachループをwhileループに置き換える必要があります。
i = 0
while i < len(somelist):
if determine(somelist[i]):
del somelist[i]
else:
i += 1
古いアイテムが削除されると、同じインデックスから新しいアイテムの値を取得する必要があるため、ifブロックではの値i
は変更されません。
この回答は元々、重複としてマークされた質問への回答として書かれました: Pythonのリストから座標を削除する
コードには2つの問題があります。
1)remove()を使用する場合、タプルを削除する必要があるのに対し、整数を削除しようとします。
2)forループは、リスト内の項目をスキップします。
コードを実行するとどうなるかを見てみましょう。
>>> L1 = [(1,2), (5,6), (-1,-2), (1,-2)]
>>> for (a,b) in L1:
... if a < 0 or b < 0:
... L1.remove(a,b)
...
Traceback (most recent call last):
File "<stdin>", line 3, in <module>
TypeError: remove() takes exactly one argument (2 given)
最初の問題は、remove()に「a」と「b」の両方を渡しているが、remove()は単一の引数のみを受け入れることです。では、どうすればremove()をリストで正しく機能させることができますか?リストの各要素が何であるかを把握する必要があります。この場合、それぞれがタプルです。これを確認するために、リストの1つの要素にアクセスしてみましょう(インデックス作成は0から始まります)。
>>> L1[1]
(5, 6)
>>> type(L1[1])
<type 'tuple'>
あはは!L1の各要素は、実際にはタプルです。これが、remove()に渡す必要があるものです。Pythonのタプルは非常に簡単で、値を括弧で囲むだけで作成されます。「a、b」はタプルではありませんが、「(a、b)」はタプルです。したがって、コードを変更して再実行します。
# The remove line now includes an extra "()" to make a tuple out of "a,b"
L1.remove((a,b))
このコードはエラーなしで実行されますが、出力されるリストを見てみましょう。
L1 is now: [(1, 2), (5, 6), (1, -2)]
(1、-2)がまだリストにあるのはなぜですか?ループを使用してリストを反復処理しながらリストを変更することは、特別な注意を払わずに行うのは非常に悪い考えです。(1、-2)がリストに残る理由は、リスト内の各項目の位置がforループの反復間で変更されたためです。上記のコードに長いリストをフィードするとどうなるか見てみましょう。
L1 = [(1,2),(5,6),(-1,-2),(1,-2),(3,4),(5,7),(-4,4),(2,1),(-3,-3),(5,-1),(0,6)]
### Outputs:
L1 is now: [(1, 2), (5, 6), (1, -2), (3, 4), (5, 7), (2, 1), (5, -1), (0, 6)]
その結果から推測できるように、条件ステートメントがtrueと評価され、リストアイテムが削除されるたびに、ループの次の反復では、値が異なるインデックスに配置されるため、リスト内の次のアイテムの評価がスキップされます。
最も直感的な解決策は、リストをコピーしてから、元のリストを繰り返し処理し、コピーのみを変更することです。あなたはこのようにそうすることを試みることができます:
L2 = L1
for (a,b) in L1:
if a < 0 or b < 0 :
L2.remove((a,b))
# Now, remove the original copy of L1 and replace with L2
print L2 is L1
del L1
L1 = L2; del L2
print ("L1 is now: ", L1)
ただし、出力は以前と同じになります。
'L1 is now: ', [(1, 2), (5, 6), (1, -2), (3, 4), (5, 7), (2, 1), (5, -1), (0, 6)]
これは、L2を作成したときに、Pythonが実際に新しいオブジェクトを作成しなかったためです。代わりに、L2をL1と同じオブジェクトに参照するだけです。これは、単に「等しい」(==)とは異なる「is」で確認できます。
>>> L2=L1
>>> L1 is L2
True
copy.copy()を使用して真のコピーを作成できます。その後、すべてが期待どおりに機能します。
import copy
L1 = [(1,2), (5,6),(-1,-2), (1,-2),(3,4),(5,7),(-4,4),(2,1),(-3,-3),(5,-1),(0,6)]
L2 = copy.copy(L1)
for (a,b) in L1:
if a < 0 or b < 0 :
L2.remove((a,b))
# Now, remove the original copy of L1 and replace with L2
del L1
L1 = L2; del L2
>>> L1 is now: [(1, 2), (5, 6), (3, 4), (5, 7), (2, 1), (0, 6)]
最後に、L1のまったく新しいコピーを作成するよりもクリーンなソリューションが1つあります。reverse()関数:
L1 = [(1,2), (5,6),(-1,-2), (1,-2),(3,4),(5,7),(-4,4),(2,1),(-3,-3),(5,-1),(0,6)]
for (a,b) in reversed(L1):
if a < 0 or b < 0 :
L1.remove((a,b))
print ("L1 is now: ", L1)
>>> L1 is now: [(1, 2), (5, 6), (3, 4), (5, 7), (2, 1), (0, 6)]
残念ながら、reversed()がどのように機能するかを適切に説明することはできません。リストが渡されると、「listreverseiterator」オブジェクトを返します。実用的な目的のために、あなたはそれをその議論の逆のコピーを作成することとして考えることができます。これが私がお勧めするソリューションです。
反復中にリストから要素を削除する場合は、whileループを使用して、削除するたびに現在のインデックスと終了インデックスを変更できるようにします。
例:
i = 0
length = len(list1)
while i < length:
if condition:
list1.remove(list1[i])
i -= 1
length -= 1
i += 1
反復中に他のことをしたい場合は、インデックス(たとえば、dictのリストがある場合に参照できることを保証します)と実際のリストアイテムの内容の両方を取得すると便利な場合があります。
inlist = [{'field1':10, 'field2':20}, {'field1':30, 'field2':15}]
for idx, i in enumerate(inlist):
do some stuff with i['field1']
if somecondition:
xlist.append(idx)
for i in reversed(xlist): del inlist[i]
enumerate
アイテムとインデックスに一度にアクセスできます。reversed
後で削除するインデックスが変更されないようにするためです。
いくつかのことを削除するだけでなく、単一のループ内のすべての要素で何かを実行したい場合に役立つ1つの可能な解決策:
alist = ['good', 'bad', 'good', 'bad', 'good']
i = 0
for x in alist[:]:
if x == 'bad':
alist.pop(i)
i -= 1
# do something cool with x or just print x
print(x)
i += 1
filter()
ビルトインとして利用可能を使用することをお勧めします。
詳細については、こちらを確認してください
for-loopingを逆に試すことができるので、some_listの場合は次のようにします。
list_len = len(some_list)
for i in range(list_len):
reverse_i = list_len - 1 - i
cur = some_list[reverse_i]
# some logic with cur element
if some_condition:
some_list.pop(reverse_i)
このようにして、インデックスが整列され、リストの更新の影響を受けません(cur要素をポップするかどうかに関係なく)。
最も効果的な方法はリスト内包表記です。多くの人が自分のケースを示します。もちろん、それはiterator
通過するための良い方法でもありfilter
ます。
Filter
関数とシーケンスを受け取ります。渡された関数を各要素に順番に適用し、関数の戻り値がであるか。Filter
であるかに応じて、要素を保持するか破棄するかを決定します。True
False
例があります(タプルのオッズを取得します):
list(filter(lambda x:x%2==1, (1, 2, 4, 5, 6, 9, 10, 15)))
# result: [1, 5, 9, 15]
注意:イテレータを処理することもできません。イテレータは、シーケンスよりも優れている場合があります。
forループはインデックスを反復処理します。
あなたがリストを持っていると考えてください、
[5, 7, 13, 29, 65, 91]
と呼ばれるリスト変数を使用していますlis
。そして、あなたは同じものを使って削除します。
あなたの変数
lis = [5, 7, 13, 29, 35, 65, 91]
0 1 2 3 4 5 6
5回目の反復中に、
35番は素数ではなかったので、リストから削除しました。
lis.remove(y)
次に、次の値(65)が前のインデックスに移動します。
lis = [5, 7, 13, 29, 65, 91]
0 1 2 3 4 5
したがって、4回目の反復完了ポインタは5番目に移動しました。
そのため、ループは前のインデックスに移動してから65をカバーしていません。
したがって、コピーではなくオリジナルを参照する別の変数にリストを参照しないでください。
ite = lis #dont do it will reference instead copy
を使用してリストのコピーを行うlist[::]
今あなたはそれが与えるでしょう、
[5, 7, 13, 29]
問題は、反復中にリストから値を削除すると、リストのインデックスが崩壊することです。
代わりに理解を試すことができます。
これは、list、tuple、dict、stringなどのすべての反復可能なものをサポートします
私は似たようなことをする必要があり、私の場合、問題はメモリでした-新しいオブジェクトとして、いくつかのことを行った後、リスト内の複数のデータセットオブジェクトをマージする必要があり、マージしていた各エントリを削除する必要がありましたそれらすべてを複製してメモリを爆破することは避けてください。私の場合、リストではなく辞書にオブジェクトがあるとうまくいきました。
`` `
k = range(5)
v = ['a','b','c','d','e']
d = {key:val for key,val in zip(k, v)}
print d
for i in range(5):
print d[i]
d.pop(i)
print d
`` `
TLDR:
私はあなたがこれを行うことを可能にするライブラリを書きました:
from fluidIter import FluidIterable
fSomeList = FluidIterable(someList)
for tup in fSomeList:
if determine(tup):
# remove 'tup' without "breaking" the iteration
fSomeList.remove(tup)
# tup has also been removed from 'someList'
# as well as 'fSomeList'
可能であれば、反復中に反復可能オブジェクトを変更する必要のない別の方法を使用することをお勧めしますが、アルゴリズムによっては、それほど単純ではない場合があります。したがって、元の質問で説明されているコードパターンが本当に必要であると確信している場合は、それが可能です。
リストだけでなく、すべての可変シーケンスで機能するはずです。
完全な答え:
編集:この回答の最後のコード例は、リスト内包表記を使用するのではなく、リストをその場で変更したい場合がある理由のユースケースを示しています。回答の最初の部分は、アレイをその場で変更する方法のチュートリアルとして機能します。
解決策は、senderleからのこの回答(関連する質問の場合)に続きます。これは、変更されたリストを反復処理するときに配列インデックスがどのように更新されるかを説明しています。以下のソリューションは、リストが変更された場合でも配列インデックスを正しく追跡するように設計されています。
ここfluidIter.py
からダウンロードしてください。これは単一のファイルなので、gitをインストールする必要はありません。インストーラーはないので、ファイルが自分自身のPythonパスにあることを確認する必要があります。コードはPython3用に記述されており、Python2ではテストされていません。 https://github.com/alanbacon/FluidIterator
from fluidIter import FluidIterable
l = [0,1,2,3,4,5,6,7,8]
fluidL = FluidIterable(l)
for i in fluidL:
print('initial state of list on this iteration: ' + str(fluidL))
print('current iteration value: ' + str(i))
print('popped value: ' + str(fluidL.pop(2)))
print(' ')
print('Final List Value: ' + str(l))
これにより、次の出力が生成されます。
initial state of list on this iteration: [0, 1, 2, 3, 4, 5, 6, 7, 8]
current iteration value: 0
popped value: 2
initial state of list on this iteration: [0, 1, 3, 4, 5, 6, 7, 8]
current iteration value: 1
popped value: 3
initial state of list on this iteration: [0, 1, 4, 5, 6, 7, 8]
current iteration value: 4
popped value: 4
initial state of list on this iteration: [0, 1, 5, 6, 7, 8]
current iteration value: 5
popped value: 5
initial state of list on this iteration: [0, 1, 6, 7, 8]
current iteration value: 6
popped value: 6
initial state of list on this iteration: [0, 1, 7, 8]
current iteration value: 7
popped value: 7
initial state of list on this iteration: [0, 1, 8]
current iteration value: 8
popped value: 8
Final List Value: [0, 1]
上記ではpop
、流体リストオブジェクトのメソッドを使用しました。、、、、、などの他の一般的な反復可能なメソッドも実装されdel fluidL[i]
て.remove
い.insert
ます。リストはスライスを使用して変更することもできます(メソッドは実装されていません)。.append
.extend
sort
reverse
唯一の条件は、いずれかの時点で、fluidL
またはl
別のリストオブジェクトに再割り当てされた場合に、コードが機能しない場合にのみ、リストを変更する必要があることです。元のfluidL
オブジェクトは引き続きforループで使用されますが、変更する範囲外になります。
すなわち
fluidL[2] = 'a' # is OK
fluidL = [0, 1, 'a', 3, 4, 5, 6, 7, 8] # is not OK
リストの現在のインデックス値にアクセスする場合、enumerateを使用することはできません。これは、forループが実行された回数のみをカウントするためです。代わりに、イテレータオブジェクトを直接使用します。
fluidArr = FluidIterable([0,1,2,3])
# get iterator first so can query the current index
fluidArrIter = fluidArr.__iter__()
for i, v in enumerate(fluidArrIter):
print('enum: ', i)
print('current val: ', v)
print('current ind: ', fluidArrIter.currentIndex)
print(fluidArr)
fluidArr.insert(0,'a')
print(' ')
print('Final List Value: ' + str(fluidArr))
これにより、次のように出力されます。
enum: 0
current val: 0
current ind: 0
[0, 1, 2, 3]
enum: 1
current val: 1
current ind: 2
['a', 0, 1, 2, 3]
enum: 2
current val: 2
current ind: 4
['a', 'a', 0, 1, 2, 3]
enum: 3
current val: 3
current ind: 6
['a', 'a', 'a', 0, 1, 2, 3]
Final List Value: ['a', 'a', 'a', 'a', 0, 1, 2, 3]
このFluidIterable
クラスは、元のリストオブジェクトのラッパーを提供するだけです。元のオブジェクトは、次のように流体オブジェクトのプロパティとしてアクセスできます。
originalList = fluidArr.fixedIterable
その他の例/テストはif __name__ is "__main__":
、の下部にあるセクションにありfluidIter.py
ます。これらは、さまざまな状況で何が起こるかを説明しているため、一見の価値があります。例:スライスを使用してリストの大きなセクションを置き換える。または、ネストされたforループで同じiterableを使用(および変更)します。
最初に述べたように、これはコードの可読性を損ない、デバッグをより困難にする複雑なソリューションです。したがって、DavidRaznickの回答で言及されているリスト内包表記などの他の解決策を最初に検討する必要があります。そうは言っても、このクラスが私にとって有用であり、削除が必要な要素のインデックスを追跡するよりも使いやすい場合があります。
編集:コメントで述べたように、この回答は、このアプローチが解決策を提供する問題を実際には提示していません。私はここでそれに対処しようとします:
リスト内包表記は、新しいリストを生成する方法を提供しますが、これらのアプローチは、リスト全体の現在の状態ではなく、各要素を個別に見る傾向があります。
すなわち
newList = [i for i in oldList if testFunc(i)]
しかし、の結果がすでにtestFunc
追加されている要素に依存している場合はどうなりnewList
ますか?または、まだその中にある要素がoldList
次に追加される可能性がありますか?リスト内包表記を使用する方法はまだあるかもしれませんが、それはその優雅さを失い始め、私にとっては、その場でリストを変更する方が簡単だと感じます。
以下のコードは、上記の問題に悩まされているアルゴリズムの一例です。アルゴリズムはリストを減らして、要素が他の要素の倍数にならないようにします。
randInts = [70, 20, 61, 80, 54, 18, 7, 18, 55, 9]
fRandInts = FluidIterable(randInts)
fRandIntsIter = fRandInts.__iter__()
# for each value in the list (outer loop)
# test against every other value in the list (inner loop)
for i in fRandIntsIter:
print(' ')
print('outer val: ', i)
innerIntsIter = fRandInts.__iter__()
for j in innerIntsIter:
innerIndex = innerIntsIter.currentIndex
# skip the element that the outloop is currently on
# because we don't want to test a value against itself
if not innerIndex == fRandIntsIter.currentIndex:
# if the test element, j, is a multiple
# of the reference element, i, then remove 'j'
if j%i == 0:
print('remove val: ', j)
# remove element in place, without breaking the
# iteration of either loop
del fRandInts[innerIndex]
# end if multiple, then remove
# end if not the same value as outer loop
# end inner loop
# end outerloop
print('')
print('final list: ', randInts)
出力と最終的な縮小リストを以下に示します。
outer val: 70
outer val: 20
remove val: 80
outer val: 61
outer val: 54
outer val: 18
remove val: 54
remove val: 18
outer val: 7
remove val: 70
outer val: 55
outer val: 9
remove val: 18
final list: [20, 61, 7, 55, 9]
他の答えは正しいですが、繰り返しているリストから削除するのは通常悪い考えです。逆反復は落とし穴を回避しますが、それを実行するコードをたどるのははるかに難しいため、通常はリスト内包表記またはを使用することをお勧めしますfilter
。
ただし、反復しているシーケンスから要素を安全に削除できる場合が1つあります。それは、反復中に1つのアイテムのみを削除する場合です。return
これは、またはを使用して確認できますbreak
。例えば:
for i, item in enumerate(lst):
if item % 4 == 0:
foo(item)
del lst[i]
break
これは、リスト内のある条件を満たすリストの最初のアイテムに副作用を伴う操作を実行し、その直後にそのアイテムをリストから削除する場合、リスト内包表記よりも理解しやすいことがよくあります。
単にリストを一度に1つのアイテムでフィルタリングする以上のことをしている状況では、反復中に反復を変更したい場合があります。
これは、事前にリストをコピーすることが正しくなく、逆反復が不可能であり、リスト内包もオプションではない例です。
""" Sieve of Eratosthenes """
def generate_primes(n):
""" Generates all primes less than n. """
primes = list(range(2,n))
idx = 0
while idx < len(primes):
p = primes[idx]
for multiple in range(p+p, n, p):
try:
primes.remove(multiple)
except ValueError:
pass #EAFP
idx += 1
yield p
私はあなたの問題を解決するための3つのアプローチを考えることができます。例として、タプルのランダムなリストを作成しますsomelist = [(1,2,3), (4,5,6), (3,6,6), (7,8,9), (15,0,0), (10,11,12)]
。私が選ぶ条件はですsum of elements of a tuple = 15
。最終的なリストには、合計が15に等しくないタプルのみが含まれます。
私が選んだのはランダムに選んだ例です。タプルのリストと私が選択した条件を自由に変更してください。
方法1.>提案したフレームワークを使用します(forループ内にコードを入力します)。del
上記の条件を満たすタプルを削除するために、との小さなコードを使用します。ただし、このメソッドは、2つの連続して配置されたタプルが指定された条件を満たす場合、(上記の条件を満たす)タプルを見逃します。
for tup in somelist:
if ( sum(tup)==15 ):
del somelist[somelist.index(tup)]
print somelist
>>> [(1, 2, 3), (3, 6, 6), (7, 8, 9), (10, 11, 12)]
方法2.>指定された条件が満たされない要素(タプル)を含む新しいリストを作成します(これは、指定された条件が満たされたリストの要素を削除するのと同じことです)。そのためのコードは次のとおりです。
newlist1 = [somelist[tup] for tup in range(len(somelist)) if(sum(somelist[tup])!=15)]
print newlist1
>>>[(1, 2, 3), (7, 8, 9), (10, 11, 12)]
方法3.>指定された条件が満たされるインデックスを検索し、それらのインデックスに対応する要素(タプル)を削除します。以下はそのためのコードです。
indices = [i for i in range(len(somelist)) if(sum(somelist[i])==15)]
newlist2 = [tup for j, tup in enumerate(somelist) if j not in indices]
print newlist2
>>>[(1, 2, 3), (7, 8, 9), (10, 11, 12)]
方法1と方法2は、方法3よりも高速です。Method2とmethod3は、method1よりも効率的です。私はmethod2を好みます。前述の例では、time(method1) : time(method2) : time(method3) = 1 : 1 : 1.7
後で新しいリストを使用する場合は、elemをNoneに設定し、後のループで次のように判断するだけです。
for i in li:
i = None
for elem in li:
if elem is None:
continue
このように、リストをコピーする必要がなく、理解しやすくなります。
本当に大きくなる可能性のあるものには、以下を使用します。
import numpy as np
orig_list = np.array([1, 2, 3, 4, 5, 100, 8, 13])
remove_me = [100, 1]
cleaned = np.delete(orig_list, remove_me)
print(cleaned)
それは他の何よりもかなり速いはずです。