Python'da bir do-while döngüsü taklit ediliyor mu?


797

Bir Python programında bir do-while döngüsü taklit etmek gerekiyor. Ne yazık ki, aşağıdaki basit kod çalışmaz:

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, tamamlandı" yerine, aşağıdaki çıktıyı yazdırır:

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

'Stop iteration' istisnasını yakalamak ve bir while döngüsünü düzgün bir şekilde kırmak için ne yapabilirim?

Böyle bir şeye neden ihtiyaç duyulabileceğinin bir örneği aşağıda sahte kod olarak gösterilmiştir.

Durum makinesi:

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
Um ... Bu uygun bir "süre" değil; bu sadece bir "sonsuza dek". "While True" ve "break" ile ilgili sorun nedir?
S.Lott

70
S. Lott: Sorusunun python'da nasıl uygulanacağıyla ilgili olduğundan eminim . Yani, kodunun tamamen doğru olmasını beklemezdim. Ayrıca, bir iş yapmaya çok yakın ... "sonsuza dek" döngüsünün sonundaki bir durumu kontrol ediyor. "Sonsuza dek" değil.
Tom

4
yani ... ilk örnek kod aslında benim için hiçbir sorun ile çalışır ve bu geri izleme alamadım. bu, kesme koşulunun yineleyici tükenmesi olduğu bir do while döngüsü için uygun bir deyimdir. genellikle, s=i.next()Hiçbiri yerine ayarladınız ve döngüden ilk geçişinizi işe yaramaz hale getirmek yerine muhtemelen ilk çalışmaları yapmalısınız.
boşalması

3
@underrun Ne yazık ki, yazı Python'un hangi sürümünün kullanıldığına dair etiketlenmemiştir - orijinal snippet, muhtemelen Python dilinin kendisindeki güncellemeler nedeniyle 2.7'yi kullanarak benim için çalışır.
Hannele

Yanıtlar:


985

Ne yapmaya çalıştığından emin değilim. Aşağıdaki gibi bir do-while döngüsü uygulayabilirsiniz:

while True:
  stuff()
  if fail_condition:
    break

Veya:

stuff()
while not fail_condition:
  stuff()

Listedeki öğeleri yazdırmak için bir do while döngüsü kullanmaya ne yapıyorsunuz? Neden sadece kullanmıyorsunuz:

for i in l:
  print i
print "done"

Güncelleme:

Hatların bir listesi var mı? Ve yinelemeyi devam ettirmek mi istiyorsun? Nasıl olur:

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

Bu, istediğiniz şeye yakın bir şey gibi mi görünüyor? Kod örneğinizle şöyle olur:

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
Bir durum makinesi oluşturmam gerekiyor. Durum makinesinde CURRENT deyimini yeniden değerlendirmek normal bir durumdur, bu yüzden sonraki öğeyi yinelemeden 'devam etmeliyim'. Ben böyle bir şey 'için s in l:' yineleme nasıl bilmiyorum :(. Do-while döngüsünde, 'devam' geçerli öğeyi, sonunda yineleme değerlendirir
grigoryvp

Yani listedeki yerinizi takip etmeniz gerekiyor mu? Bu şekilde aynı duruma döndüğünüzde kaldığınız yerden devam edebilir misiniz? Biraz daha bağlam verin. Listeye bir dizin kullanarak daha iyi olabilir gibi görünüyor.
Tom

Teşekkürler, sahte kodunuz hakkında yorum yaptım ... örneğiniz biraz kötü görünüyor, çünkü siz hangi eyalette olursanız olun aynı şekilde işliyor görünüyorsunuz. Ayrıca, bu gerçek kod yorumları işliyor musunuz? Eğik çizgili dizeleriniz varsa ne olur? ie: print "blah // <- bu seni mahvediyor mu?"
Tom

4
Python'un bir do-while döngüsüne sahip olmaması bir utanç. Python KURU, ha?
Kr0e

43
Ayrıca resmi duruş / gerekçelendirme için PEP 315'e bakın : "Dil kullanıcılarının, bir do-while döngüsü uygun olduğunda bir while-True formunu iç if-break ile kullanmaları önerilir."
dtk

311

İşte bir do-while döngüsünü taklit etmenin çok basit bir yolu:

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

Bir do-while döngüsünün temel özellikleri, döngü gövdesinin her zaman en az bir kez yürütülmesi ve koşulun döngü gövdesinin altında değerlendirilmesidir. Burada gösterilen kontrol yapısı, istisna veya kesme ifadesine gerek kalmadan her ikisini de gerçekleştirir. Bir ekstra Boole değişkeni ekler.


11
Her zaman fazladan bir boole değişkeni eklemez. Genellikle hali hazırda durumu test edilebilen bir şey (ler) vardır.
martineau

14
Bu çözümü en çok sevmem sebebi, başka bir koşul eklememesi, hala sadece bir döngü olması ve yardımcı değişken için iyi bir isim seçmeniz durumunda tüm yapı oldukça açıktır.
Roberto

4
NOT: Bu orijinal soruyu ele alırken, bu yaklaşım kullanmaktan daha az esnektir break. Özellikle, SONRASI gerekli bir mantık varsa test_loop_condition(), işimiz bittikten sonra yürütülmemelidir, sarılmalıdır if condition:. BTW, conditionbelirsizdir. Daha açıklayıcı: moreveya notDone.
ToolmakerSteve

7
@ToolmakerSteve Kabul etmiyorum. Nadiren breakdöngülerde kullanıyorum ve bunu koruduğum kodla karşılaştığımda, döngünün, çoğu zaman, onsuz yazılmış olabileceğini görüyorum. Sunulan çözüm, IMO, python'da bir do while yapısını temsil etmenin en açık yoludur.
nonsensickle

1
İdeal olarak, koşul has_no_errorsveya gibi açıklayıcı bir şey olarak adlandırılacaktır end_reached(bu durumda döngü başlayacaktırwhile not end_reached
Josiah Yoder

75

Aşağıdaki kodum, arasındaki temel farkı vurgulayan yararlı bir uygulama olabilir vs anladığım kadarıyla.

Bu durumda, her zaman döngüden en az bir kez geçersiniz.

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

2
Doğru cevabı, I'de tartışıyorlar. Ayrıca bloklarda denemede / dışında güvenli kullanım için kırılmayı önler .
Zv_oDD

jit / optimizer ilk geçişten sonra first_pass'ı tekrar test etmekten kaçınır mı? aksi halde, sinir bozucu olsa da, belki de küçük bir performans sorunu
olurdu

2
@markhahn bu gerçekten küçük ama böyle detayların veriyorsan, sen döngü içinde 2 boole intervert edebilirsiniz: while condition or first_pass:. Daha sonra conditionher zaman önce değerlendirilir ve genel first_passolarak sadece iki kez değerlendirilir (ilk ve son yineleme). Döngüden conditionönce ne istersen başlatmayı unutma .
pLOPeGG

HM, ilginç ben aslında durumu başlatmak zorunda kalmamak için bilerek başka bir şekilde seçmişti ve bu nedenle kodda minimum değişiklikler gerektiriyordu. Demek
evan54

33

İstisna döngüyü kıracaktır, böylece döngünün dışında da işleyebilirsiniz.

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

Kod ile sorun breakiç davranış excepttanımlanmamış olmasıdır sanırım . Genellikle breakbu nedenle örneğin, sadece bir seviye yukarı gider breakiçeride trydoğrudan gider finally(varsa) yetersiz try, fakat döngünün dışında.

İlgili PEP: http://www.python.org/dev/peps/pep-3136
İlgili soru: İç içe döngülerin kopması


8
İstenmeyen istisnaları yakalayabilmeniz için, sadece istisnalarınızı atmayı umduğunuz şeyleri deneme deyiminin içinde bulundurmanız iyi bir uygulamadır.
Paggas

7
@PiPeep: RTFM, EAFP'yi arayın.
vartec

2
@PiPeep: Sorun değil, sadece bazı diller için geçerli olanın diğer diller için doğru olmayabileceğini unutmayın. Python, istisnaların yoğun kullanımı için optimize edilmiştir.
vartec

5
break ve devam, try / hariç / nihayet deyiminin herhangi bir yan tümcesinde mükemmel bir şekilde tanımlanmıştır. Basitçe onları görmezden gelirler ve içerdiği while veya döngü için uygun şekilde bir sonraki yinelemeden ayrılır veya devam ederler. Döngü yapılarının bileşenleri olarak, yalnızca while ve for ifadeleriyle ilgilidirler ve en içteki döngüye ulaşmadan önce bir sınıfa veya def deyimine girdiklerinde bir sözdizimi hatası tetiklerler. If, with ve try ifadelerini yok sayarlar.
ncoghlan

1
.. ki bu önemli bir durum
javadba

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

->

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

Bir işlev yapabilirsiniz:

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

Ama 1) Çirkin. 2) Durum şeyler tarafından doldurulacak gerekiyordu bir parametresi olan bir işlev olmalıdır (bu tek nedeni değil klasik while döngüsü kullanmak.)


5
Yazmak while True: stuff(); if not condition(): breakçok iyi bir fikir. Teşekkür ederim!
Noctis Skytower

2
@ZeD, neden 1) çirkin? Tamam, IMHO
Sergey Lossev

@SergeyLossev Programın mantığını kavramak zor olacaktır, çünkü aralarında çok fazla 'şeyler' kodunuz varsa, başlangıçta sonsuz bir döngü olarak görünür.
exic

16

İşte farklı bir modelin daha çılgın bir çözümü - coroutines kullanarak. Kod hala çok benzer, ancak önemli bir fark var; hiç çıkış koşulu yok! Koroutin (gerçekten koroutin zinciri) veri ile beslemeyi bıraktığınızda durur.

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)

Yukarıdaki kod, tüm kodları tuples olarak toplar ve orijinal kod tokensarasında .append()ve .add()orijinal kodda hiçbir fark olmadığını varsayarım .


4
Bunu bugün Python 3.x'e nasıl yazardınız?
Noctis Skytower

14

Bunu yapma şeklim şöyle ...

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

Bu bana basit bir çözüm gibi geliyor, burada daha önce görmediğime şaşırdım. Bu açıkça tersine çevrilebilir

while not condition:

vb.


"Şimdiye kadar burada görmediğime şaşırdım" diyorsunuz - ama diyelim ki Powderflask'ın 2010 çözümünden bir fark görmüyorum. Bu tamamen aynı. ("condition = True while koşulu: # loop body here condition = test_loop_condition () # loop sonu")
cslotty

10

try ifadeleri içeren bir do - while döngüsü için

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()

alternatif olarak, 'nihayet' maddesine gerek olmadığında

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
Ew. Bir mola kullanmaktan çok daha çirkin görünüyor.
mattdm

5
Bu zekidir, ancak stuffbir işlev olması veya kod gövdesinin tekrarlanması gerekir.
Noctis Skytower

12
İhtiyaç duyulan tek şey olduğunu while condition:, çünkü is Trueima edilir.
martineau

2
conditioniç değişkenine bağlıysa bu başarısız olur stuff(), çünkü o değişken o anda tanımlanmamıştır.
yo '

5
Aynı mantık değil, çünkü koşulun son yinelemesinde! = Doğru: Kodu son kez çağırır. Nerede Yapılacak Süre olarak kodu önce bir kez çağırır, ardından yeniden çalıştırmadan önce koşulu kontrol eder. Süre Yap: bloğu bir kez yürütür; daha sonra bu cevabı kontrol edip tekrar çalıştırın; kontrol edin ve tekrar çalıştırın; daha sonra bir kez kod bloğu yürütün . Büyük fark!
Zv_oDD

7

Hızlı kesmek:

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

Şöyle kullanın:

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

3

Neden sadece yapmıyorsun

for s in l :
    print s
print "done"

?


1
Bir durum makinesi oluşturmam gerekiyor. Durum makinesinde CURRENT deyimini yeniden değerlendirmek normal bir durumdur, bu yüzden sonraki öğeyi yinelemeden 'devam etmeliyim'. Ben böyle bir şey yapmak için nasıl bilmiyorum 'l s için:'.. Yineleme :( döngü, 'devam' in while-do re-değerlendirecek yineleme sonunda, cari öğeyi
grigoryvp

o zaman, devlet makineniz için bazı sahte kodlar tanımlayabilir misiniz, böylece sizi en iyi pitonik çözüme yönlendirebilir miyiz? Devlet makineleri hakkında fazla bir şey bilmiyorum (ve muhtemelen sadece ben değilim), bu yüzden bize algoritmanız hakkında biraz bilgi verirseniz, bu size yardımcı olmaktan daha kolay olacaktır.
Martin

For loop gibi şeyler için çalışmaz: a = fun () ise a == 'zxc': sleep (10) a = fun ()
harry

Bu tamamen bir boolean durumu kontrol noktasını özlüyor
javadba

1

Bunun yardımcı olup olmadığına bakın:

İstisna işleyicinin içine bir bayrak yerleştirin ve s üzerinde çalışmadan önce kontrol edin.

flagBreak = false;
while True :

    if flagBreak : break

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

print "done"

3
Kullanılarak while not flagBreak:ve kaldırılarak basitleştirilebilir if (flagBreak) : break.
martineau

1
Ben adı verilen değişkenlerden kaçınırım flag- True veya False değerinin ne anlama geldiğini çıkartamam Bunun yerine doneveya kullanın endOfIteration. Kod dönüşür while not done: ....
IceArdor

1

Bir kaynak uygun olmadığında veya istisna oluşturan benzer bir şeyde döngü yaptığınız bir senaryodaysanız, şöyle bir şey kullanabilirsiniz:

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

Benim için tipik bir while döngüsü şöyle olacak:

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

Eğer durum çok garanti, eğer başka bir koşul kümesi döngü için ise while döngüsü içinde bir for..loop içerebilir .

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.