def make_list_foo_only(size):
"""
foo+初期インデックスを生成:foo0, foo1, ..., fooX
"""
l = list("foo{:97d}".format(x) for x in range(size))
return l
def make_list_bar_only(size):
"""
bar+初期インデックスを生成: bar0, bar1, ..., barX
"""
l = list("bar{:97d}".format(x) for x in range(size))
return l
def make_list_alternate_foo_bar(size):
"""
foo,bar+初期インデックスが交互を生成: foo0, bar1, ..., fooX or barX
"""
l = list("bar{:97d}".format(x) if x % 2 else "foo{:97d}".format(x) for x in range(size))
return l
def make_list_bar50_foo50(size):
"""
前半をbar+初期インデックス、後半foo+初期インデックスを生成: bar0, bar1, ..., fooX
"""
l = list("bar{:97d}".format(x) if x < size / 2 else "foo{:97d}".format(x) for x in range(size))
return l
def make_list_foo50_bar50(size):
"""
前半をfoo+初期インデックス、後半bar+初期インデックスを生成: foo0, foo1, ..., barX
"""
l = list("foo{:97d}".format(x) if x < size / 2 else "bar{:97d}".format(x) for x in range(size))
return l
def list_remove(l):
"""
公式ドキュメントの方法で、スライスによる一時コピー有り。
e.startswith("f")以外の条件で削除しないケースが有る場合はNG
シンプルだけど項目数が増えると糞遅くなる。
"""
for i, e in enumerate(l[:]):
if e.startswith("f"):
# あれこれ
l.remove(e)
return l
def list_del_reversed(l):
"""
末尾から削除、スライスによる一時コピーは不要。
あれこれ処理するのが後ろからになる事が許容できるならこれが良好。
"""
for i in reversed(range(len(l))): # xrangeは3でrangeに要置換なので使用せず
e = l[i]
if e.startswith("f"):
# あれこれ
del l[i]
return l
駄目っです。リファレンス参照どぞ (スコア:1)
[:]でコピー作らないと駄目よー
詳しくはリファレンス参照:2.6 [python.org] 3だけど日本語化されてる。中身は同じ [python.org]
Re: (スコア:-1)
l[i]を削除した時点でlと[:]でiがズレますよ。
Re: (スコア:0)
そのとおり。リファレンス参照は関係ない話で、原因は 「iがズレますよ。」ですね
"foo bar baz"で[:]を使うと動くように見えるのはタダの勘違いです
"foo bar baz"ならもとのコードでも[:]でもどちらでも bar baz と
正しくうごいているようにみえます
別コメントにあるように"foo foo foo"で試せば[:]でもエラーがでます
Re:駄目っです。リファレンス参照どぞ (スコア:1)
"foo bar baz"ならもとのコードでも[:]でもどちらでも bar baz と
正しくうごいているようにみえます
今回のテストデータでは正しくうごいているようにみえるだけです。
"foo foo bar"というテストデータでテストすれば解りますが、
for中に要素数が変わると"foo bar"と2個目のfで始まる奴が残ります。
私がリンクしたリファレンスのNote(注釈)を読まないから「厄介なバグ」を見逃したり、作りこみます。
iがズレるのは、
del l[i]
を
l.remove(e)
みたいにiに依存しない形で変更する事で回避可能です。
もしくはオフセットを持たすか、whileで手動でカウントアップするか。
removeの場合は意図的に残したみたいなパターンで重複が有り得る場合は破綻しますけど。
例えば1個目のfooは残す場合、foo,foo,barな場合、1個目が消えちゃいます。
例:
l = "foo foo foo".split(" ")
for i, e in enumerate(l[:]):
if e.startswith("f"):
いろいろ
l.remove(e)
print(l)
実行結果:
['bar']
これがメモリ喰ったり遅いけど、元のコードからあまり変わらず解りやすい方法。
データ数が多い等でdelで頑張りたいなら、#3899763 [srad.jp]さんのように逆順で回すのが楽です。
どれがベストかは削除の条件やらユニーク有無やらどの程度のデータ量が有るかとか解らないのでなんとも。
# そもそも私もPythonは滅多に触らないのでもっと良い書き方が有るかもです。
Re:駄目っです。リファレンス参照どぞ (スコア:1)
テストデータが間違ってるじゃねーか。
l = "foo foo foo".split(" ")
を
l = "foo foo bar".split(" ")
に置換してお読みください。
Re: (スコア:0)
要素を一意にしてからチェックするのは処理時間的に無駄な気がする
Re:駄目っです。リファレンス参照どぞ (スコア:1)
要素を一意にしてからチェックするのは処理時間的に無駄な気がする
他の要因、例えば別のリストが居てそっちの内容も影響するとか等、
条件判定でeを残すパターンが有るとかなければ一意でなくても良いです。
remove(e)ですが、処理的にリスト内の探索の時間が無駄になります。
ですが、それが問題にならないくらい途中を削除する事がNGです。
削除する度に内部で削除した以降の要素を手前に持ってくる移動処理をして、リストが再構築されてると思わしき挙動なのでめちゃ重です。
リストが一万件越えて大半を削除する事になると体感できるぐらいになってくるかと。
# ちゃんと計測したことないけど、新規リスト作って、最後に入れ替えた方が早いと思う。
# でも、今回は「in placeで削除」らしいので対象外。
でも、せいぜい数千件程度とか、削除の頻度はそんなにないとか、使い捨てでたまにしか走らないプログラムなら必要十分。
後は処理時間の削減やコードの見通し悪化やどの程度コードを書く時間かけるかの費用対効果です。
使い捨てスクリプトやバッチに数分悩んだら、遅いアルゴリズムでも処理終わって次の作業出来てるかもしれないし。
実測してみた。 (スコア:1)
要素を一意にしてからチェックするのは処理時間的に無駄な気がする
# ちゃんと計測したことないけど、新規リスト作って、最後に入れ替えた方が早いと思う。
気になったのでちょろっと書いた糞コードと実測結果晒しておきます。
ちゃんとデバッグしてないからバグってたり、動かなかったらごめんなさい。
Python使いが見たら罵倒されると思う。
# coding: utf-8
import timeit
def make_list_foo_only(size):
"""
foo+初期インデックスを生成:foo0, foo1, ..., fooX
"""
l = list("foo{:97d}".format(x) for x in range(size))
return l
def make_list_bar_only(size):
"""
bar+初期インデックスを生成: bar0, bar1, ..., barX
"""
l = list("bar{:97d}".format(x) for x in range(size))
return l
def make_list_alternate_foo_bar(size):
"""
foo,bar+初期インデックスが交互を生成: foo0, bar1, ..., fooX or barX
"""
l = list("bar{:97d}".format(x) if x % 2 else "foo{:97d}".format(x) for x in range(size))
return l
def make_list_bar50_foo50(size):
"""
前半をbar+初期インデックス、後半foo+初期インデックスを生成: bar0, bar1, ..., fooX
"""
l = list("bar{:97d}".format(x) if x < size / 2 else "foo{:97d}".format(x) for x in range(size))
return l
def make_list_foo50_bar50(size):
"""
前半をfoo+初期インデックス、後半bar+初期インデックスを生成: foo0, foo1, ..., barX
"""
l = list("foo{:97d}".format(x) if x < size / 2 else "bar{:97d}".format(x) for x in range(size))
return l
def list_remove(l):
"""
公式ドキュメントの方法で、スライスによる一時コピー有り。
e.startswith("f")以外の条件で削除しないケースが有る場合はNG
シンプルだけど項目数が増えると糞遅くなる。
"""
for i, e in enumerate(l[:]):
if e.startswith("f"):
# あれこれ
l.remove(e)
return l
def list_del_reversed(l):
"""
末尾から削除、スライスによる一時コピーは不要。
あれこれ処理するのが後ろからになる事が許容できるならこれが良好。
"""
for i in reversed(range(len(l))): # xrangeは3でrangeに要置換なので使用せず
e = l[i]
if e.startswith("f"):
# あれこれ
del l[i]
return l
def list_del_offset(l):
"""
リスト先頭から処理が必要な場合の妥協案。
スライスによる一時コピー有り。
"""