質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.48%
for

for文は、様々なプログラミング言語で使われている制御構造です。for文に定義している条件から外れるまで、for文内の命令文を繰り返し実行します。

Python 3.x

Python 3はPythonプログラミング言語の最新バージョンであり、2008年12月3日にリリースされました。

Q&A

解決済

9回答

2213閲覧

for文で、フラグを使わずに、if条件に合致した後のイテレーションをとばしたい

siruku6

総合スコア1382

for

for文は、様々なプログラミング言語で使われている制御構造です。for文に定義している条件から外れるまで、for文内の命令文を繰り返し実行します。

Python 3.x

Python 3はPythonプログラミング言語の最新バージョンであり、2008年12月3日にリリースされました。

3グッド

5クリップ

投稿2019/01/06 04:11

編集2019/01/06 05:01

pythonでfor文を使っているときに、ある条件を満たしたときだけ、イテレーションを一気に飛ばしたいということはありませんか?

ちなみに、飛ばすタイミングは、条件を満たしたイテレーションにおいてではなくて、条件を満たした後の次のイテレーションにおいてです。

サンプルをご覧いただきたいです><

試したこと

python

1for i in range(1, 11): 2 print(i) 3 if i == 3: 4 continue # 違う 5 # continue は一つとばすだけ 6 # 2つ3つ先にiを飛ばしたい
# 結果 1 2 3 4 ←これいらない 5 6 7 8 9 10

望んでいる結果

# 4を飛ばしたい 1 2 3 5 6 7 8 9 10

試したこと2

python

1for i in range(1, 11): 2 print(i) 3 if i == 3: 4 i += 1
# 結果 1 2 3 4 ←これいらない 5 6 7 8 9 10

補足

サンプルとして、4を飛ばしていますが4はあくまでも例です。
私が行いたいのは、

  • for文の中で使用されている i の値を直接操作すること

です。
フラグを使用すればできるのはわかりますが、不要な変数を追加したくないので、フラグを使用しない方法でお願いしたいです。

そもそもそういったことはできないのかもしれませんが、もし知っている方いらっしゃったら、ご教示お願いいたします。。。

firedfly, naotohori👍を押しています

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答9

0

公式ドキュメントのitertoolsのレシピにイテレータをn個(あるいはすべて)消費するconsume関数のサンプルが書いてあります。
https://docs.python.org/ja/3.6/library/itertools.html#itertools-recipes

Python

1import collections 2from itertools import islice 3 4 5def consume(iterator, n=None): 6 "Advance the iterator n-steps ahead. If n is None, consume entirely." 7 # Use functions that consume iterators at C speed. 8 if n is None: 9 # feed the entire iterator into a zero-length deque 10 collections.deque(iterator, maxlen=0) 11 else: 12 # advance to the empty slice starting at position n 13 next(islice(iterator, n, n), None) 14 15 16# ここまでitertoolsのドキュメントから引用 17 18 19it = iter(range(1, 11)) 20for i in it: 21 print(i) 22 if i == 3: 23 consume(it, 2)

とした時の結果が

1 2 3 6 7 8 9 10

ですよ。

イテラブルイテレータの差はわかっている必要があるテクニックですけれども。

投稿2019/01/06 05:26

編集2019/01/06 05:36
quickquip

総合スコア11038

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

siruku6

2019/01/06 05:53

回答ありがとうございます。 回答スピードも考慮してベストアンサーはつけられませんでしたが、+させていただきました! 個人的にはすごく使ってみたい(よだれが出るほど)のですが、会社の新卒入社の人とかにも触ってもらえるようにしたいので、他のプログラミング言語でも一般的な方法を採用しようと思います><
quickquip

2019/01/08 00:01 編集

私はKSwordOfHasteさんの冒頭の回答がほぼ正解だと思います。イテレータは"他のプログラミング言語でも一般的な方法"だと思うので。
KSwordOfHaste

2019/01/08 04:24

dequeやisliceで期待通りの副作用を及ぼしながらほとんど余計なメモリーを消費しない点、自分のレベルではこれを安心して使おうと考えるほどはPythonの関連機能に自信を持ててないなぁと感じました (><) 自分の回答ではアプリケーション論理の中で直接nextを呼んでいます。対してquiquiさんが挙げてくださったconsumeはiteratorに対してどう副作用を及ぼすかの実装がいわば「隠蔽」されており、アプリケーション論理の中で書くならnextを直接呼び出すのではなく、consumeを使うことが妥当に思えました。そういう意味で自分はquiquiさん回答がBAにふさわしいと思います。 ちなみにBAは変更できるはずですので、よろしかったらquiquiさんの方へどうぞ!>siruku6さん
quickquip

2019/01/08 05:11

"1つか2つ読み飛ばす"ことしかないのであれば、私ならnextを1回か2回呼びますよ。上で「冒頭の回答がほぼ正解だと思います」と書いたとおりです。標準関数ですし。
quickquip

2019/01/08 05:13

それはそれとして、itertoolsのレシピは一通り目を通しておくとプログラミングの幅が広がる、とてもよいサンプルなので紹介したかったのでした。
KSwordOfHaste

2019/01/08 05:35

nextに低水準な香りを感じていたのですが、その(中途半端な?)自分の感覚が「違っていた」ということのようです。 itertoolsレシピを読んでおけば違った感覚をもてたと思いました。コメントありがとうございました。
guest

0

ベストアンサー

追記: コメントでご指摘いただいたStopIterationに関して不自然と思える部分、MyIteratorの__next__の定義がおかしかった点を訂正いたしました。
ご指摘ありがとうございました。> hayataka2049さん、quiquiさん


for文の内部のメカニズムを考えると以下のようなヘンテコなコードが思い浮かびました。実際にやってみると動きました(Python3)。しかし自分はこんなコードを見たらプログラマーの正気を疑うこのようなコードは推奨できないと思います...

python

1loop_iter = iter(range(1, 11)) 2for i in loop_iter: 3 print(i) 4 if i == 3: 5 next(loop_iter) # for文が次の要素4を求める前に事前に消費してしまう 6 next(loop_iter) # さらに5も飛ばす 7 # 追記: ご指摘いただいたとおり、next(loop_iter, None)の方がよかったです。 8# ==> 91 102 113 126 137 14...

pythonのfor文の繰り返しを支配するのはinの後ろのiterableに対してiter(iterable)で取得できる「繰り返しを司るもの=iteraor」です。そいつに対してnextを呼び出すことで次々に返される値を制御変数(上記ではi)に束縛しながらループ本体を実行する仕組みになっています。

「試したこと2」でやっておられるように制御変数自体の値をループ本体でどういじっても効果がないのはそのためです。CやJavaなどのfor文とはPythonのそれは根本的に動作のメカニズムが違うのですね。

さてiteratorはpythonのスクリプト上には登場せずインタープリタが内部的にこっそりと行っています。上のコードは「実はiteratorもiterableの一種とみなされており、iter(iteratorインスタンス)の結果はiteratorインスタンスそのものになることを悪用したものです。

自分はこんな病的なコードを書くぐらいならあきらめてwhileを使うべきと思います。

i = 1 while i < 11: print(i) if i == 3: i += 2

そういうことを多用する特殊なアプリケーションがあったとして、特別なiteratorを自分で定義することなら...うーん、アリかも知れませんし、ナシかもしれません。個人的にはどっちかというとナシかなと思います。

# もちょっとお行儀よくcollections.abc.Iteratorを継承すべきかも... class MyIterator: def __init__(self, iterable): self.iterator = iter(iterable) def __next__(self, default=None): # if default is None: # return next(self.iterator) # else: # try: # return next(self.iterator) # except StopIteration: # return default # 上記実装はビルトイン関数nextの機能を誤って__next__に持たせてしまっており # 誤りだったと思います。単に以下のようにする方が妥当と思います。 self.iterator.__next__() def skip(self, n): for _ in range(n): # try: # next(self.iterator) # except StopIteration: # break next(self.iterator, None) # ご指摘から、よりシンプルな記述に変更しました def __iter__(self): return self it = MyIterator(range(1, 11)) for i in it: print(i) if i == 3: it.skip(2)

投稿2019/01/06 05:12

編集2019/01/07 12:13
KSwordOfHaste

総合スコア18394

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

hayataka2049

2019/01/06 05:19

前にも似たような話題がありましたが、nextで次に進めようとすると、たとえばi==9だった場合、forがStopIterationを受け取ってくれないのでそのままraiseされてくる問題があります。 https://teratail.com/questions/158382
siruku6

2019/01/06 05:22

非常に勉強になります! >こんなコードを見たらプログラマーの正気を疑うと思います... >こんな病的なコードを書くぐらいならあきらめてwhileを使うべきと思います。 私も同様に感じました。 自分以外誰も見ないコードだったとしても、次に見たときに嫌な気分になる気がします。 大人しく `while` か フラグ付きの `for` を使うことにします。 一番他の方の参考になると感じましたので、ベストアンサーをつけさせていただきました!
KSwordOfHaste

2019/01/06 11:02

hayataka2049さんコメントありがとうございます。 i==9のときの話ですがおっしゃるとおりですね。特にそれについては気にせず例を挙げただけのつもりでした。MyIteratorみたいなものを作るとしたらそういうのも配慮しとくのかなと思いMyIterator.skipでは一応余計なStopIterationが発生しないようには書いときましたがそもそもあまりお勧めでないと思ったので特にコメントもしてませんでした... --- 離籍のためコメントおそくなり失礼しました。
quickquip

2019/01/06 23:37

StopIterationの問題は next(loop_iter, None) と書くだけで支障ないでしょう。冒頭のコードはあまりやりたくはない類いではあるけれど"普通のコード"だと私は思います。
KSwordOfHaste

2019/01/07 12:15 編集

コメントありがとうございます。 おっしゃるとおりnextの第二引数で事足りました。 自分はfor文を支配するiteratorがコード上表に出てこないインタープリタ内部の制御情報と捉えたほうがよい(動きのメカニズムは仕様からわかるけどそれを操作するようなことが推奨・許容されているか怪しい)と思ったのでやるべきでないと思ったのですが「メカニズムがきちんと公開されている以上それを利用してよい」という見方もあるかも知れません。そのあたり自分には意見以上のことは言えないと気づきました。ゆえに「正気を疑う」は言い過ぎだったです。 --- いただいたご指摘について自分なりに回答へ反映させていただきました。
Kenji.Noguchi

2019/01/08 00:22

`loop_iter.next()` の方が好み。StopIterationの問題は残りますが。
KSwordOfHaste

2019/01/08 01:19

ビルトイン関数の呼び出しよりメソッド呼び出しを好むという意味でしたら、特殊メソッドの呼び出しloop_iter.__next__()になると思います。 loop_iter.__next__()もnext(loop_iter)もインタープリタの内部動作に踏み込みこんだ記述であることはかわりないですが、next(loop_iter)の方が若干低水準な香りが薄い気がします。 「1 .__add__(2)」とは書きたくなくて「1 + 2」と書くのを好むのと同じ理由です。 一方低水準なライブラリーの実装で用いる場合は気にならないと思います。
hayataka2049

2019/01/08 02:31 編集

nextの引数の件は見落としていました・・・ >`loop_iter.next()` の方が好み。StopIterationの問題は残りますが。 python2ではnextだったものがpython3では__next__にされた経緯があったと思います。組み込み関数nextはどちらにせよあり、つまりはアンダーバー付きの名前にされたのも特殊メソッド呼ばないでくれという意思表明ですね。
KSwordOfHaste

2019/01/08 03:26

> python2では そういう意図だったのですか!読み取りそこなってましたorz コメントありがとうございました >hayataka2049さん 頓珍漢なコメントしてしまい失礼しました >Kenji.Noguchiさん
guest

0

この場合、イメージ的にはrange(1, 11)の中身[1,2,...,10]が先にあって(厳密には若干異なりますが)、ループごとにi = 1, i = 2と代入されていく・・・という挙動になります。iを書き換えようとしても、次のループで順番通り上書き代入されてしまうので無意味です。

やるとしたら、こんな感じにするしかないと思います。

python

1cnt = 0 2for i in range(1, 11): 3 if cnt == 0: 4 print(i) 5 else: 6 cnt -= 1 7 if i == 3: 8 cnt = 1

か、最初からwhileで書いて自分でカウンタを管理するか。

python

1cnt = 0 2target = list(range(1, 11)) # listに変換しておかないと意図どおり取り扱えない場合があります 3 4while cnt < len(target): 5 i = target[cnt] 6 print(i) 7 if i == 3: 8 cnt += 1 9 cnt += 1

投稿2019/01/06 05:04

hayataka2049

総合スコア30933

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

siruku6

2019/01/06 05:12

限界へ挑戦していただいてありがとうございます! whileならカウンタを直接操作できる、ということですね。 せっかくなのですが、whileだと一見して可読性が下がりそうなので、forとフラグを使用する方法にしようかなと考えております。 (もう少し待ってみます)
guest

0

if で合致したときになにかフラグをセットし、
次のループでフラグがセットされてれば、continue するという処理をする、とか
で可能ですね

投稿2019/01/06 04:28

y_waiwai

総合スコア87774

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

siruku6

2019/01/06 04:53

ありがとうございます。 フラグは使いたくないのです。 for ループで使用されている i の値を操作したいのです。
y_waiwai

2019/01/06 04:59

C言語のforならループ中で制御変数をいじれるんですけどねえ
siruku6

2019/01/06 05:02

そうですよね。 私の頭が古いのかもしれません...>< コメントありがとうございます!
guest

0

う〜ん、やっぱり

  1. 処理対象となるデータのマーキング
  2. 処理対象となるデータの抽出
  3. 本処理

の流れが自然かな。(機能を分割した方が良い。簡単にグチャりそう。)
理由としては

a) 抽出条件の仕様変更、仕様追加の対応が大変。
b) 連続したスキップに対応できない可能性がある。
(3を見て、次の4をスキップするのは良いけど、5をスキップするには4をチェックする必要がある。)
などが挙げられます。

投稿2019/01/09 02:15

編集2019/01/09 03:50
o8q

総合スコア18

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

0

4を飛ばしたいのでしたら

python

1for i in range(1, 11): 2 if i == 4: 3 continue 4 else: 5 print(i)

スキップ

4のとき次の1回スキップ

python

1# スキップ数 2n = 0 3 4for i in range(1, 11): 5 6 if n > 0: 7 n -= 1 8 continue 9 10 elif i == 4: 11 # スキップ数 12 n = 1 13 continue 14 15 else: 16 print(i)

次をスキップ

3の時次n回スキップ

python

1n = 0 2 3for i in range(1, 11): 4 5 if n > 0: 6 n -= 1 7 continue 8 9 elif i == 3: 10 # スキップ数 11 n = 1 12 13 print(i)

投稿2019/01/06 04:26

編集2019/01/06 05:02
barobaro

総合スコア1286

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

siruku6

2019/01/06 04:55

回答ありがとうございます。 せっかくなのですが、飛ばしたいのは4ではありません。 4はあくまでも例になります。 特定の条件に合致したときに、次のイテレーションを任意の複数回飛ばすことが目的になります。
siruku6

2019/01/06 05:05

回答の編集ありがとうございます。 nが、フラグ兼スキップ数になっているサンプルですね! i の値をいじりたいのですが、pythonにはそういう処理が想定されてないのかもしれないですね。
barobaro

2019/01/06 05:15

iはリストのインデックスではのないのでいじっても何も意味がないのでは
siruku6

2019/01/06 05:29

>iはリストのインデックスではのないのでいじっても何も意味がないのでは hayataka2049さんのおかげで、意味がないことがわかりました…。 barobaroさんもコメントありがとうございます。
guest

0

ただ単純に1個先を飛ばす

python

1iterable = iter(range(1, 11)) 2for i in iterable: 3 print(i) 4 if i == 3: 5 next(iterable, None)

投稿2019/01/12 05:37

編集2019/01/12 05:51
YouheiSakurai

総合スコア6142

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

0

###リストにしてremoveでむしり取る

【python】listをforループで回してremoveしたら思い通りにならない - 静かなる名辞

もしくは

8.3. for 文 — Python 3.6.5 ドキュメント

を参照

注釈 ループ中でのシーケンスの変更には微妙な問題があります (これはミュータブルなシーケンス、すなわちリストなどでのみ起こります)。
・・・略・・・
このことから、スイート中でシーケンスから現在の (または以前の) 要素を除去すると、(次の要素のインデクスは、すでに取り扱った現在の要素のインデクスになるために) 次の要素が飛ばされることになります。

上記を悪用します。
ある条件を3、飛ばしたい数を4とします。

コード1

python

1# coding: utf-8 2lst = list(range(1,11)) 3 4for i in lst: 5 print(i) 6 if i == 3: 7 lst.remove(i)
結果 1 2 3 5 6 7 8 9 10

3は出力して4を飛ばせました。

###リストにしてremoveでむしり取るver2

コード1を改造して、飛ばしたい数を複数にしてみます。
ある条件を3、飛ばしたい数を4、5とします。

コード1.1

python

1# coding: utf-8 2lst = list(range(1,11)) 3 4for i in lst: 5 print(i) 6 if i == 3: 7 idx = lst.index(i) 8 map(lst.remove(lst[idx]), range(2))
結果 1 2 3 5 6 7 8 9 10

3は出力して、4,5を飛ばせました。

###都合のいいprintができる関数を作っておく。

ある条件を3、飛ばしたい数を4、5としています。

コード2

python

1# coding: utf-8 2 3def my_print(i,condition=3,skip=2): 4 if i in range(condition+1,condition+1+skip): 5 pass 6 else: 7 print(i) 8 9for i in range(1, 11): 10 my_print(i)
結果 1 2 3 6 7 8 9 10

3は出力して、4,5を飛ばせました。

投稿2019/01/08 00:59

編集2019/01/08 13:21
Yoshitaket.

総合スコア25

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

o8q

2019/01/08 10:34

コード1は lst = list(range(1,11)) lst.remove(3) for i in lst: ... じゃ、あかんの?
Yoshitaket.

2019/01/08 12:35 編集

はい、おそらく。 o9qさんのコードを想像で埋めると以下のコード3になるかと思います。※違ったらすみません。 [コード3](https://paiza.io/projects/zIZS-FlSTSH6LWA41koAIw?language=python3) ```python # coding: utf-8 lst = list(range(1,11)) lst.remove(3) for i in lst: print(i) ``` ``` 実行結果 1 2 4 #条件に合致した3が飛ばれされて、4が出力されてしまう。 5 6 7 8 9 10 ``` 質問者siruku6さんが「望んでいる結果」と「試したこと2」に書いている内容から、 「3に合致した場合、次の4を飛ばしたい」なので、4が出力されてしまう挙動はNGかなと。
guest

0

... ある条件を満たしたときだけ、イテレーションを一気に飛ばしたい ...

i を変化させる処理と、ループ body の処理を分離してみました。

以下では 3 つの ループがありますが、
最初のものは、質問にあるような動作を、
2 番目のものは、2 ずつ i を増加させる動作を、
3 編目のものは、 i が 進んだり戻ったりを繰り返すような動作を
しています。

ccc.py

python3

1def range_with_step_func(head, tail, step_func = None): 2 ans = [] 3 i = head 4 while i < tail: 5 if head <= i: 6 ans.append(i) 7 i += step_func(i) 8 return ans 9 10# i が 3 なら、その次は +3 した 6 にする、 11# i != 3 なら, その次は +1 した値にする、 12# という処理を lambda 記述する。 13for i in range_with_step_func(1, 11, lambda x: 3 if x == 3 else 1): 14 print(i, "", end="") 15print() 16 17for i in range_with_step_func(1, 11, lambda x: 2): 18 print(i, "", end="") 19print() 20 21for i in range_with_step_func(1, 11, lambda x: 2 if x % 3 == 1 else -1 if x % 3 == 0 else 2): 22 print(i, "", end="") 23print() 24

実行例
イメージ説明

投稿2019/01/06 09:47

編集2019/01/12 13:30
katoy

総合スコア22324

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問