Pythonでdo-whileループをエミュレートしますか?


798

Pythonプログラムでdo-whileループをエミュレートする必要があります。残念ながら、次の単純なコードは機能しません。

list_of_ints = [ 1, 2, 3 ]
iterator = list_of_ints.__iter__()
element = None

while True:
  if element:
    print element

  try:
    element = iterator.next()
  except StopIteration:
    break

print "done"

「1,2,3、done」の代わりに、次の出力を出力します。

[stdout:]1
[stdout:]2
[stdout:]3
None['Traceback (most recent call last):
', '  File "test_python.py", line 8, in <module>
    s = i.next()
', 'StopIteration
']

「反復の停止」例外をキャッチし、whileループを適切に解除するには、どうすればよいですか?

なぜそのようなものが必要になるかの例は、疑似コードとして以下に示されています。

状態機械:

s = ""
while True :
  if state is STATE_CODE :
    if "//" in s :
      tokens.add( TOKEN_COMMENT, s.split( "//" )[1] )
      state = STATE_COMMENT
    else :
      tokens.add( TOKEN_CODE, s )
  if state is STATE_COMMENT :
    if "//" in s :
      tokens.append( TOKEN_COMMENT, s.split( "//" )[1] )
    else
      state = STATE_CODE
      # Re-evaluate same line
      continue
  try :
    s = i.next()
  except StopIteration :
    break

4
ええと...それは適切な「do-while」ではありません。それは単に「永遠に行うこと」です。「while True」と「break」の何が問題になっていますか?
S.Lott、2009

70
S.ロット:彼の質問はPythonでdoを実装する方法についてだったと思います。したがって、私は彼のコードが完全に正しいとは思わないでしょう。また、彼はdo doに非常に近いです...彼は、「永久」ループの最後で条件をチェックして、抜ける必要があるかどうかを確認しています。それは「永久に」ではありません。
トム

4
したがって...最初のサンプルコードは実際には問題なく動作し、そのトレースバックを取得できません。これは、ブレーク条件がイテレータの枯渇であるdo whileループの適切なイディオムです。通常は、s=i.next()Noneではなく設定し、ループを最初に通過するだけでは役に立たないのではなく、初期作業を行う可能性があります。
アンダーラン

3
@underrun残念ながら、投稿には使用されているPythonのバージョンがタグ付けされていません。おそらくPython言語自体の更新が原因で、元のスニペットは2.7を使用しても機能します。
Hannele

回答:


985

あなたが何をしようとしているのかわかりません。次のようなdo-whileループを実装できます。

while True:
  stuff()
  if fail_condition:
    break

または:

stuff()
while not fail_condition:
  stuff()

do whileループを使用してリストの内容を印刷しようとしていますか?なぜ使用しないのですか?

for i in l:
  print i
print "done"

更新:

それであなたは行のリストを持っていますか?そして、あなたはそれを繰り返し続けたいですか?どうですか:

for s in l: 
  while True: 
    stuff() 
    # use a "break" instead of s = i.next()

それはあなたが望むものに近いもののように見えますか?あなたのコード例では、それは次のようになります:

for s in some_list:
  while True:
    if state is STATE_CODE:
      if "//" in s:
        tokens.add( TOKEN_COMMENT, s.split( "//" )[1] )
        state = STATE_COMMENT
      else :
        tokens.add( TOKEN_CODE, s )
    if state is STATE_COMMENT:
      if "//" in s:
        tokens.append( TOKEN_COMMENT, s.split( "//" )[1] )
        break # get next s
      else:
        state = STATE_CODE
        # re-evaluate same line
        # continues automatically

1
ステートマシンを作成する必要があります。状態マシンでは、CURRENTステートメントを再評価するのは通常のケースなので、次の項目を繰り返さずに「続行」する必要があります。'for s in l:'反復でそのようなことを行う方法がわかりません。反復:(。do-whileループでは、 'continue'は現在のアイテムを再評価します。最後に反復します
grigoryvp

リストで自分の場所を追跡する必要があるということですか?そうすれば、同じ状態に戻ったときに、中断したところから再開できますか?もう少しコンテキストを説明します。リストへのインデックスを使用したほうがよいようです。
トム

ありがとう、私はあなたの疑似コードにコメントしました...あなたがどのような状態にいても「//」を同じ方法で処理しているように見えるので、あなたの例はちょっと悪いようです。また、コメントを処理しているこの本当のコードですか?スラッシュを含む文字列がある場合はどうなりますか?例:print "blah // <-それはあなたを台無しにしていますか?"
トム

4
Pythonにdo-whileループがないのは残念です。PythonはDRYですね。
Kr0e 2013

43
公式のスタンス/正当化については、PEP 315も参照してください。「この言語のユーザーは、do-whileループが適切であった場合、内側のif-breakでwhile-Trueフォームを使用することをお勧めします。」
dtk

311

do-whileループをエミュレートする非常に簡単な方法を次に示します。

condition = True
while condition:
    # loop body here
    condition = test_loop_condition()
# end of loop

do-whileループの主要な機能は、ループ本体が常に少なくとも1回実行されること、および条件がループ本体の下部で評価されることです。ここに示す制御構造は、例外やbreakステートメントを必要とせずに、これらの両方を実現します。ブール変数が1つ追加されています。


11
常にブール変数が追加されるわけではありません。多くの場合、状態をテストできる既存のものが存在します。
martineau

14
私がこのソリューションを最も気に入っている理由は、別の条件が追加されず、それでも1サイクルにすぎないためです。ヘルパー変数に適切な名前を付ければ、構造全体が非常に明確になります。
Roberto

4
注:これは元の質問に対処しますが、このアプローチはを使用する場合よりも柔軟性が低くなりbreakます。具体的には、AFTER test_loop_condition()に必要なロジックがあり、一度実行したら実行してはならない場合は、にラップする必要がありif condition:ます。ところで、conditionあいまいです。より説明的な:moreまたはnotDone
ToolmakerSteve

7
@ToolmakerSteve私は同意しません。breakループで使用することはほとんどなく、維持しているコードでこのループに遭遇すると、ほとんどの場合、ループはループなしで作成された可能性があることがわかります。提示されたソリューションは、IMO、Pythonで構築しながらdoを表す最も明確な方法です。
nonsensickle

1
理想的には、条件は次のように、何かの記述という名前になりますhas_no_errorsend_reachedループが開始すると、その場合には(while not end_reached
ジョサイアYoderの

75

以下の私のコードは有用な実装である可能性があり、主な違いを強調しています 私が理解しているように。

したがって、この1つのケースでは、常に少なくとも1回はループを通過します。

first_pass = True
while first_pass or condition:
    first_pass = False
    do_stuff()

2
正解だと私は主張する。さらに、try / exceptブロックで安全に使用するためにbreakを回避します。
Zv_oDD 2016

jit / optimizerは最初のパスの後でfirst_passを再テストすることを避けますか?それ以外の場合は、迷惑な、しかしおそらくマイナー、パフォーマンスの問題だろう
markhahn

2
@markhahnこれは本当にマイナーですが、そのような詳細を気にする場合は、ループ内の2つのブール値を逆にすることができますwhile condition or first_pass:。次にcondition、常に最初に評価され、全体first_passが2回だけ評価されます(最初と最後の反復)。conditionループの前に初期化することを忘れないでください。
pLOPeGG 2019年

HM、興味深いのですが、実際には条件を初期化する必要がなく、コードの変更を最小限に抑える必要があるため、意図的に反対の方法で選択していました。それは私があなたの要点を見ると言った
evan54

33

例外はループを壊すので、ループの外で処理することもできます。

try:
  while True:
    if s:
      print s
    s = i.next()
except StopIteration:   
  pass

あなたのコードの問題は、break内部の動作がexcept定義されていないことだと思います。一般breakに、1レベルだけ上に移動します。たとえば、breakinside tryは、ループの外ではなく、直接outに移動しますfinally(存在する場合)try

関連PEP:http : //www.python.org/dev/peps/pep-3136
関連質問:ネストされたループからの脱出


8
不要な例外をキャッチしないように、例外をスローすることが予想されるものだけをtryステートメント内に含めることをお勧めします。
Paggas 2009年

7
@PiPeep:RTFM、EAFPを検索します。
vartec

2
@PiPeep:問題ありません。一部の言語には当てはまること、他の言語には当てはまらないこともあります。Pythonは、例外を集中的に使用するように最適化されています。
vartec

5
break and continueは、try / except / finallyステートメントのどの句でも完全に定義されています。それらは単にそれらを無視し、必要に応じて、含まれているwhileまたはforループから抜けるか、次の反復に進みます。ループ構造のコンポーネントとして、それらはwhileおよびforステートメントにのみ関連し、最も内側のループに到達する前にクラスまたはdefステートメントに実行されると構文エラーをトリガーします。if、with、tryステートメントは無視されます。
ncoghlan '18

1
..これは重要なケースです
javadba

33
do {
  stuff()
} while (condition())

->

while True:
  stuff()
  if not condition():
    break

次の機能を実行できます。

def do_while(stuff, condition):
  while condition(stuff()):
    pass

しかし、1)それは醜いです。2)条件は、1つのパラメーターを持つ関数である必要があります。これは、もので満たされることになっています(これが、従来のwhileループを使用しない唯一の理由です)。


5
書くことwhile True: stuff(); if not condition(): breakはとても良い考えです。ありがとうございました!
Noctisスカイタワー2012

2
@ ZeD、1)醜いのはなぜですか?それはかなり大丈夫です、私見
セルゲイロスセフ

@SergeyLossevプログラムのロジックを把握するのは困難です。これは、間に大量の「スタッフ」コードがあると、最初は無限ループとして表示されるためです。
exic

16

これは、コルーチンを使用した、異なるパターンのよりクレイジーなソリューションです。コードはまだ非常によく似ていますが、重要な違いが1つあります。終了条件はまったくありません!コルーチン(実際にはコルーチンのチェーン)は、データの供給を停止すると停止します。

def coroutine(func):
    """Coroutine decorator

    Coroutines must be started, advanced to their first "yield" point,
    and this decorator does this automatically.
    """
    def startcr(*ar, **kw):
        cr = func(*ar, **kw)
        cr.next()
        return cr
    return startcr

@coroutine
def collector(storage):
    """Act as "sink" and collect all sent in @storage"""
    while True:
        storage.append((yield))

@coroutine      
def state_machine(sink):
    """ .send() new parts to be tokenized by the state machine,
    tokens are passed on to @sink
    """ 
    s = ""
    state = STATE_CODE
    while True: 
        if state is STATE_CODE :
            if "//" in s :
                sink.send((TOKEN_COMMENT, s.split( "//" )[1] ))
                state = STATE_COMMENT
            else :
                sink.send(( TOKEN_CODE, s ))
        if state is STATE_COMMENT :
            if "//" in s :
                sink.send(( TOKEN_COMMENT, s.split( "//" )[1] ))
            else
                state = STATE_CODE
                # re-evaluate same line
                continue
        s = (yield)

tokens = []
sm = state_machine(collector(tokens))
for piece in i:
    sm.send(piece)

上記のコードはすべてのトークンをタプルとして収集します。元のコードとのtokens間には違いがないと思います。.append().add()


4
今日、Python 3.xでこれをどのように書きますか?
Noctisスカイタワー2012

14

私がこれを行った方法は次のとおりです...

condition = True
while condition:
     do_stuff()
     condition = (<something that evaluates to True or False>)

これは単純な解決策であるように私には思えますが、ここではまだ見たことがないことに驚いています。これは明らかに反転することもできます

while not condition:


「ここで見たことがないのには驚きました」と言いますが、たとえば、2010年のpowderflaskのソリューションと何の違いもありません。まったく同じです。( "condition = True while condition:#loop body here condition = test_loop_condition()#end of loop")
cslotty

10

for-doステートメントを含むwhileループ

loop = True
while loop:
    generic_stuff()
    try:
        questionable_stuff()
#       to break from successful completion
#       loop = False  
    except:
        optional_stuff()
#       to break from unsuccessful completion - 
#       the case referenced in the OP's question
        loop = False
   finally:
        more_generic_stuff()

または、「finally」句が不要な場合

while True:
    generic_stuff()
    try:
        questionable_stuff()
#       to break from successful completion
#       break  
    except:
        optional_stuff()
#       to break from unsuccessful completion - 
#       the case referenced in the OP's question
        break

7
while condition is True: 
  stuff()
else:
  stuff()

8
ええ。これは、休憩を使用するよりもかなり醜いようです。
mattdm 2012年

5
これは賢い方法ですがstuff、関数にするか、コード本体を繰り返す必要があります。
Noctisスカイタワー2012

12
必要なのはwhile condition:is True暗黙であるためです。
martineau

2
この変数はその時点で定義されていないため、のcondition内部変数に依存している場合、これは失敗しますstuff()
yo '

5
同じロジックではありません。条件!= Trueの場合、最後の反復で、コードを最後に呼び出します。Do Whileとして、コードを最初に1回呼び出し、次に条件をチェックしてから再実行します。実行中:ブロックを1回実行します。次に、チェックして再実行します。この回答:チェックして再実行します。次に、コードブロックを1回実行します。大きな違い!
Zv_oDD 2016

7

クイックハック:

def dowhile(func = None, condition = None):
    if not func or not condition:
        return
    else:
        func()
        while condition():
            func()

そのように使用してください:

>>> x = 10
>>> def f():
...     global x
...     x = x - 1
>>> def c():
        global x
        return x > 0
>>> dowhile(f, c)
>>> print x
0

3

どうしてやらないの?

for s in l :
    print s
print "done"


1
ステートマシンを作成する必要があります。状態マシンでは、CURRENTステートメントを再評価するのは通常のケースなので、次の項目を繰り返さずに「続行」する必要があります。私はそのようなことを行う方法がわからない「リットル中のための」反復を:(では何-whileループ、再評価され、現在のアイテム、最後の反復「を継続」。。
grigoryvp

次に、状態マシンの擬似コードを定義して、最高のpythonicソリューションに向けてヒントを与えることができますか?ステートマシンについてはあまり詳しくないので(おそらく1つだけではないでしょう)、アルゴリズムについて少し教えていただければ、こちらの方が助けになります。
マーティン

forループは次のような場合には機能しません:a = fun()while a == 'zxc':sleep(10)a = fun()
harry

これはブール条件をチェックするポイントを完全に逃します
javadba

1

これが役立つかどうかを確認してください:

例外ハンドラ内にフラグを設定し、sで作業する前にフラグを確認します。

flagBreak = false;
while True :

    if flagBreak : break

    if s :
        print s
    try :
        s = i.next()
    except StopIteration :
        flagBreak = true

print "done"

3
を使用while not flagBreak:して削除することで簡略化できますif (flagBreak) : break
martineau

1
という名前の変数は避けますflag-True値またはFalse値の意味を推測できません。代わりに、doneまたはを使用してくださいendOfIteration。コードはに変わりwhile not done: ...ます。
IceArdor 2014年

1

リソースが利用できない、または例外をスローする同様の何かの間にループしているシナリオでは、次のようなものを使用できます

import time

while True:
    try:
       f = open('some/path', 'r')
    except IOError:
       print('File could not be read. Retrying in 5 seconds')   
       time.sleep(5)
    else:
       break

0

私にとって、典型的なwhileループは次のようになります:

xBool = True
# A counter to force a condition (eg. yCount = some integer value)

while xBool:
    # set up the condition (eg. if yCount > 0):
        (Do something)
        yCount = yCount - 1
    else:
        # (condition is not met, set xBool False)
        xBool = False

whileループ内にfor..loopを含めることもできます(状況に応じて)、別の条件セットをループするため。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.