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 の値を直接操作すること
です。
フラグを使用すればできるのはわかりますが、不要な変数を追加したくないので、フラグを使用しない方法でお願いしたいです。
そもそもそういったことはできないのかもしれませんが、もし知っている方いらっしゃったら、ご教示お願いいたします。。。
気になる質問をクリップする
クリップした質問は、後からいつでもMYページで確認できます。
またクリップした質問に回答があった際、通知やメールを受け取ることができます。
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
回答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総合スコア11038
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総合スコア18394
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
2019/01/06 05:22
2019/01/06 05:27
2019/01/06 11:02
2019/01/06 23:37
2019/01/07 12:15 編集
2019/01/08 00:22
2019/01/08 01:19
2019/01/08 02:31 編集
2019/01/08 03:26
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
総合スコア30933
0
if で合致したときになにかフラグをセットし、
次のループでフラグがセットされてれば、continue するという処理をする、とか
で可能ですね
投稿2019/01/06 04:28
総合スコア87774
0
う〜ん、やっぱり
- 処理対象となるデータのマーキング
- 処理対象となるデータの抽出
- 本処理
の流れが自然かな。(機能を分割した方が良い。簡単にグチャりそう。)
理由としては
a) 抽出条件の仕様変更、仕様追加の対応が大変。
b) 連続したスキップに対応できない可能性がある。
(3を見て、次の4をスキップするのは良いけど、5をスキップするには4をチェックする必要がある。)
などが挙げられます。
投稿2019/01/09 02:15
編集2019/01/09 03:50総合スコア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総合スコア1286
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
2019/01/06 04:55
2019/01/06 05:05
2019/01/06 05:15
2019/01/06 05:29
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総合スコア6142
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
0
###リストにしてremoveでむしり取る
【python】listをforループで回してremoveしたら思い通りにならない - 静かなる名辞
もしくは
8.3. for 文 — Python 3.6.5 ドキュメント
を参照
注釈 ループ中でのシーケンスの変更には微妙な問題があります (これはミュータブルなシーケンス、すなわちリストなどでのみ起こります)。
・・・略・・・
このことから、スイート中でシーケンスから現在の (または以前の) 要素を除去すると、(次の要素のインデクスは、すでに取り扱った現在の要素のインデクスになるために) 次の要素が飛ばされることになります。
上記を悪用します。
ある条件を3、飛ばしたい数を4とします。
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とします。
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としています。
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総合スコア25
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
2019/01/08 10:34
2019/01/08 12:35 編集
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総合スコア22324
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
あなたの回答
tips
太字
斜体
打ち消し線
見出し
引用テキストの挿入
コードの挿入
リンクの挿入
リストの挿入
番号リストの挿入
表の挿入
水平線の挿入
プレビュー
質問の解決につながる回答をしましょう。 サンプルコードなど、より具体的な説明があると質問者の理解の助けになります。 また、読む側のことを考えた、分かりやすい文章を心がけましょう。
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
2019/01/06 05:53
2019/01/08 00:01 編集
2019/01/08 04:24
2019/01/08 05:11
2019/01/08 05:13
2019/01/08 05:35