パスワードを忘れた? アカウント作成
14694727 journal
日記

tuneoの日記: Pythonでこういう書き方はしていいんだっけ? 24

日記 by tuneo

使用する処理系は諸般の事情でPython2.6。久々のPythonで頭がボケてる……。

やりたいのはリストの操作なんだけど、ループでリストの要素を頭からなめて、特定の条件を満たす場合はあれこれ処理した後でin placeで削除したい。

たとえば文字列を空白で分割して、部分文字列が"f"で始まっていた場合はリストから削除する、という処理だ。


l = " ".split("foo bar baz")
for i, e in enumerate(l):
        if e.startswith("f"):
                あれこれ
                del l[i]
print l

というコードは正当で、常に["bar", "baz"]を返すことを期待していいんだったっけ?

……もちろん上記の例のごときはリスト内包表記なりfilter関数を使って書けばいいのだが、本チャンのコードはちゃんとややこしい。

この議論は賞味期限が切れたので、アーカイブ化されています。 新たにコメントを付けることはできません。
  • [:]でコピー作らないと駄目よー
    詳しくはリファレンス参照:2.6 [python.org] 3だけど日本語化されてる。中身は同じ [python.org]

    • shallow copyをなめるループを回しながらオリジナルを削除すればよいということですかね。

      for i, e in enumerate(l[:]):
          if e.startswith("f"):
              del l[i]

      親コメント
      • その場合リストの"i個目"がオリジナルとズレるので「del l[i]」がバグります。
        eがユニークで有る事を保証できるならeをキーにremoveするのが手っ取り早いです。(#3899890 [srad.jp])
        i個目に重要な意味が有る/データ数が多い等でdelを使うなら、末尾からループさせるか、iに削除した分のマイナス補正して下さい。(del l[i-削除済数]とか)
        もしくは、forループを諦めてwhileにし、削除時はiをカウントアップしないようなアルゴリズムにして下さい。

        どれを選ぶかは、お好みで。

        親コメント
    • by Anonymous Coward

      リファレンス参照って馬から落馬チックだな

  • by Anonymous Coward on 2020年10月02日 22時27分 (#3899575)

    間違いがふたつ有りますね

    1つ目は一行目。正しくは l = "foo bar baz".split(" ") ですね

    2つ目は本題で l = "foo foo foo".split(" ") とすれば直ぐに駄目なことが解ります

    • by tuneo (2938) on 2020年10月03日 11時37分 (#3899797) ホームページ 日記

      ・「これはやってもいいんだっけ?」という疑問を持つこと自体が「間違い」
      ・「いろんな要素があるリストの特定の要素だけを削除するコード」をお題にするときは「全部の要素が削除対象」というケースを例に挙げる必要がある

      貴重なご意見ありがとうございました。

      親コメント
      • by kei100 (5854) on 2020年10月03日 14時47分 (#3899895)

        #3899575 [srad.jp]さんの言いたいことが曲解されてるような気がするのでツッコミます。

        ・「これはやってもいいんだっけ?」という疑問を持つこと自体が「間違い」

        ロジック的にバグってるという「間違い」ですよ。

        ・「いろんな要素があるリストの特定の要素だけを削除するコード」をお題にするときは「全部の要素が削除対象」というケースを例に挙げる必要がある

        貴重なご意見ありがとうございました。

        重要なのは全部fooな事でなく、fooが2連続しているという事です。
        削除漏れが起きるテストパターンの例示ですね。
        原理:foo1個目削除→foo元2個目が1個目になる→次のループで、foo元3個目がfoo2個目として渡ってくる。→foo元2個目が削除されずに残る。
        って事です。

        親コメント
        • by Anonymous Coward

          むしろ全てに疑問を持つべきですよね。
          エクセルじゃだめなんですかとかメモ帳じゃだめなのかそもそもやらなきゃいけないのかレベルから始まり本当にそれでできるのかメモリは足りるか時間は足りるかエトセトラ。

        • by Anonymous Coward

          kei100さんは教えるの下手くそなのでツッコみますね

          L = [1, 2]
          del L[0]
          del L[1]

          この話はこれだけで説明できます。リファレンス参照とか関係無い話はやめましょう

          • by kei100 (5854) on 2020年10月04日 7時25分 (#3900201)

            ですよねー
            教えるの下手くそですよね。

            リファレンス参照って、Pythonのドキュメント(Forの注釈)を読めって意味だったんですけどね。

            ところで、foreach中にコレクションの要素を削除という、.NETなら例外で落ちる所業 [microsoft.com]してるけど、
            Pythonは例外で死なずに内部カウンタが狂うだけで中途半端に動く&回避策が書いて有るけど関係無いって話なんです?

            親コメント
            • by Anonymous Coward

              あなたの最初のコメントは
              > [:]でコピー作らないと駄目よー
              > 詳しくはリファレンス参照:2.6 [python.org] 3だけど日本語化されてる。中身は同じ [python.org]
              です

              「コピー作らないと駄目よー」って勘違いしてた訳で、それを誤魔化すのは止めましょうよ

              • by kei100 (5854) on 2020年10月07日 13時11分 (#3902128)

                そちらこそ、ちゃんとリンク先の注釈読んでます?

                シーケンス全体のスライスを使って一時的なコピーを作ることで避けられます。

                ディープコピーしてもよいけど、丸ごとスライスする事で、一時コピーをforに渡してるんですよ?
                コピーを渡さないと内部カウンタがずれるので、その事込みでロジックを組まないと削除漏れが起きます。
                # しかも明文化されてない内部仕様に依存するので将来動くかさえ不確実。

                親コメント
          • by Anonymous Coward

            その説明で理解できるなら件のコードを書いた時点で気づくと思う。
            自然言語の説明はやっぱ必要。

      • by Anonymous Coward

        目的を達成できるかという点ではすでに出ているようにだめ。
        Pythonでこういうやり方が良いのかという点でもだめ。
        Pythonなら判定を
        "foo bar baz".startswith('f') or ' f' in "foo bar baz"
        とかにしたほうがPythonぽい

typodupeerror

開いた括弧は必ず閉じる -- あるプログラマー

読み込み中...