เลียนแบบลูป do-while ใน Python หรือไม่?


797

ฉันต้องการเลียนแบบลูป do-while ในโปรแกรม Python น่าเสียดายที่รหัสตรงไปตรงมาไม่ทำงาน:

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 ทำแล้ว" มันพิมพ์ผลลัพธ์ต่อไปนี้:

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

ฉันจะทำอะไรได้บ้างเพื่อให้ได้ข้อยกเว้น 'หยุดการทำซ้ำ' และแบ่งลูปชั่วขณะอย่างถูกต้อง?

ตัวอย่างของสิ่งที่อาจเป็นสิ่งจำเป็นที่แสดงด้านล่างเป็นรหัสเทียม

เครื่องรัฐ:

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" ที่เหมาะสม นั่นเป็นเพียง "ทำตลอดไป" เกิดอะไรขึ้นกับ "ในขณะที่ True" และ "break"
S.Lott

70
เอส Lott: ฉันค่อนข้างแน่ใจว่าคำถามของเขาเกี่ยวกับวิธีการใช้ในหลาม ดังนั้นฉันไม่คาดหวังว่ารหัสของเขาจะถูกต้องสมบูรณ์ นอกจากนี้เขายังอยู่ใกล้สิ่งที่ต้องทำ ... เขากำลังตรวจสอบเงื่อนไขในตอนท้ายของ "ตลอดไป" เพื่อดูว่าเขาควรจะแยกออกจากกัน มันไม่ใช่ "ทำตลอดกาล"
Tom

4
ดังนั้น ... โค้ดตัวอย่างเริ่มต้นของคุณใช้งานได้จริงสำหรับฉันโดยไม่มีปัญหาและฉันไม่ได้รับการติดตามกลับ นั่นเป็นสำนวนที่เหมาะสมสำหรับการทำในขณะที่วงที่เงื่อนไขการแบ่งเป็นอ่อนเพลีย iterator โดยทั่วไปแล้วคุณจะต้องตั้งค่าs=i.next()มากกว่าไม่มีและอาจจะเริ่มต้นทำงานมากกว่าที่จะทำให้การผ่านครั้งแรกของคุณผ่านลูปไร้ประโยชน์แม้ว่า
underrun

3
@underrun แต่น่าเสียดายที่โพสต์ไม่ได้ติดแท็กว่ามีการใช้งาน Python เวอร์ชันใด - ส่วนย่อยดั้งเดิมนั้นเหมาะกับฉันด้วยเช่นกันโดยใช้ 2.7 น่าจะเป็นเพราะการอัพเดตภาษา Python เอง
Hannele

คำตอบ:


985

ฉันไม่แน่ใจว่าคุณกำลังพยายามทำอะไร คุณสามารถใช้ลูป do-while แบบนี้:

while True:
  stuff()
  if fail_condition:
    break

หรือ:

stuff()
while not fail_condition:
  stuff()

คุณกำลังพยายามใช้สิ่งใดขณะทำซ้ำเพื่อพิมพ์เนื้อหาในรายการ ทำไมไม่ใช้เพียง:

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 ใน l:' iteration :(. ใน do-while loop, 'ดำเนินการต่อ' จะทำการประเมินรายการปัจจุบันอีกครั้งการวนซ้ำเมื่อสิ้นสุด
grigoryvp

คุณหมายถึงคุณต้องติดตามตำแหน่งของคุณในรายการหรือไม่? ด้วยวิธีนี้เมื่อคุณกลับสู่สถานะเดิมคุณสามารถเลือกจุดที่ค้างไว้ได้? ให้บริบทมากกว่านี้หน่อย ดูเหมือนว่าคุณอาจจะดีกว่าเมื่อใช้ดัชนีในรายการ
Tom

ขอบคุณฉันแสดงความคิดเห็นใน pseudocode ของคุณ ... ตัวอย่างของคุณดูเหมือนจะไม่ดีเนื่องจากคุณดูเหมือนจะจัดการ "//" ในลักษณะเดียวกันไม่ว่าคุณจะอยู่ในสถานะใดนอกจากนี้รหัสจริงนี้ที่คุณกำลังประมวลผลความคิดเห็นหรือไม่ ถ้าคุณมีสายอักขระที่มีเครื่องหมายทับ? เช่น: พิมพ์ "blah // <- ทำให้คุณงงหรือเปล่า
Tom

4
มันเป็นความอัปยศที่หลามไม่มีห่วง do-while Python คือ DRY ใช่มั้ย
Kr0e

43
โปรดดูเพิ่มเติมที่PEP 315สำหรับท่าทาง / เหตุผลอย่างเป็นทางการ: "แนะนำให้ผู้ใช้ภาษาใช้รูปแบบ while-True พร้อมกับ if-break ด้านในเมื่อวงวน do-while มีความเหมาะสม"
dtk

311

นี่เป็นวิธีง่ายๆในการเลียนแบบลูป do-while:

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

คุณสมบัติที่สำคัญของลูป do-while คือลูป body จะดำเนินการอย่างน้อยหนึ่งครั้งและจะประเมินสภาพที่ด้านล่างของลูปบอดี้ โครงสร้างการควบคุมแสดงไว้ที่นี่ทำให้ทั้งสองอย่างสำเร็จโดยไม่จำเป็นต้องมีข้อยกเว้นหรือตัดคำสั่ง มันแนะนำตัวแปรบูลีนพิเศษหนึ่งตัว


11
มันไม่ได้เพิ่มตัวแปรบูลีนพิเศษเสมอไป บ่อยครั้งที่มีบางสิ่งที่มีอยู่แล้วซึ่งสามารถทดสอบสถานะได้
martineau

14
เหตุผลที่ฉันชอบวิธีนี้มากที่สุดก็คือมันไม่ได้เพิ่มเงื่อนไขอื่นมันยังคงเป็นเพียงหนึ่งรอบและถ้าคุณเลือกชื่อที่ดีสำหรับตัวแปรตัวช่วยโครงสร้างทั้งหมดค่อนข้างชัดเจน
Roberto

4
หมายเหตุ: breakในขณะนี้จะอยู่ที่คำถามเดิมวิธีนี้มีความยืดหยุ่นน้อยกว่าการใช้ โดยเฉพาะถ้ามีตรรกะที่จำเป็นหลังจากที่ไม่ควรจะดำเนินการทันทีที่เราจะทำก็จะต้องมีการห่อtest_loop_condition() if condition:BTW conditionคลุมเครือ อธิบายเพิ่มเติม: หรือmore notDone
ToolmakerSteve

7
@ToolmakerSteve ฉันไม่เห็นด้วย ฉันไม่ค่อยได้ใช้breakลูปและเมื่อฉันพบมันในโค้ดที่ฉันเก็บไว้ฉันพบว่าลูปบ่อยที่สุดสามารถเขียนได้หากไม่มีมัน โซลูชันที่นำเสนอคือ IMO วิธีที่ชัดเจนที่สุดในการแสดงสิ่งที่ต้องทำขณะที่สร้างในไพ ธ อน
nonsensickle

1
ตามหลักแล้วเงื่อนไขจะถูกตั้งชื่อบางอย่างที่อธิบายเช่นhas_no_errorsหรือend_reached(ในกรณีนี้การวนซ้ำจะเริ่มต้นwhile not end_reached
Josiah Yoder

75

รหัสของฉันด้านล่างอาจเป็นการใช้งานที่มีประโยชน์โดยเน้นความแตกต่างหลักระหว่าง VS ตามที่ฉันเข้าใจ

ดังนั้นในกรณีนี้คุณต้องผ่านลูปอย่างน้อยหนึ่งครั้งเสมอ

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

2
คำตอบที่ถูกต้องฉันเถียง รวมถึงหลีกเลี่ยงการแตกเพื่อความปลอดภัยในการใช้งานลอง / ยกเว้นบล็อค
Zv_oDD

jit / optimizer หลีกเลี่ยงการทดสอบ first_pass อีกครั้งหลังจากผ่านครั้งแรกหรือไม่ ไม่อย่างนั้นจะเป็นเรื่องที่น่ารำคาญแม้ว่าจะเป็นเรื่องเล็กน้อยเรื่องประสิทธิภาพการทำงาน
markhahn

2
@markhahn นี้เป็นจริงเล็กน้อย แต่ถ้าคุณดูแลรายละเอียดดังกล่าวคุณสามารถ intervert 2 booleans while condition or first_pass:ในวง: จากนั้นconditionจะถูกประเมินก่อนเสมอและโดยรวมfirst_passจะถูกประเมินเพียงสองครั้งเท่านั้น อย่าลืมที่จะเริ่มต้นconditionก่อนที่จะวนรอบสิ่งที่คุณต้องการ
pLOPeGG

HM ที่น่าสนใจจริง ๆ แล้วฉันได้เลือกวิธีอื่น ๆ โดยไม่ต้องมีเงื่อนไขในการเริ่มต้นและต้องมีการเปลี่ยนแปลงเล็กน้อยในรหัส ที่กล่าวว่าฉันเห็นจุดของคุณ
evan54

33

ข้อยกเว้นจะทำให้เกิดการวนซ้ำดังนั้นคุณอาจจัดการนอกวงได้เช่นกัน

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

ฉันเดาว่าปัญหาเกี่ยวกับรหัสของคุณคือพฤติกรรมbreakภายในexceptไม่ได้ถูกกำหนดไว้ โดยทั่วไปแล้วbreakจะเพิ่มขึ้นเพียงระดับเดียวเท่านั้นเช่นbreakภายในนั้นtryไปที่finally(ถ้ามี) โดยตรงนอกtryแต่ไม่ออกจากลูป

PEP ที่เกี่ยวข้อง: http://www.python.org/dev/peps/pep-3136
คำถามที่เกี่ยวข้อง: การแบ่งออกเป็นลูปซ้อนกัน


8
เป็นวิธีปฏิบัติที่ดีแม้ว่าจะมีเพียงในแถลงการณ์ลองสิ่งที่คุณคาดหวังว่าจะทำให้เกิดข้อยกเว้น
Paggas

7
@PiPeep: RTFM ค้นหา EAFP
vartec

2
@PiPeep: ไม่มีปัญหาเพียงจำไว้ว่าสิ่งที่เป็นจริงสำหรับบางภาษาอาจไม่เป็นจริงสำหรับคนอื่น Python เหมาะสำหรับการใช้งานข้อยกเว้นอย่างเข้มข้น
vartec

5
ทำลายและดำเนินการต่อได้อย่างสมบูรณ์แบบที่กำหนดไว้อย่างดีในส่วนของคำสั่งลอง / ยกเว้น / ในที่สุด พวกเขาไม่สนใจพวกเขาและแยกออกหรือย้ายไปที่การทำซ้ำต่อไปของการบรรจุในขณะที่หรือสำหรับวงตามความเหมาะสม ในฐานะที่เป็นส่วนประกอบของการวนลูปโครงสร้างมันเกี่ยวข้องเฉพาะกับ while และ for statement และทริกเกอร์ข้อผิดพลาดทางไวยากรณ์หากพวกมันทำงานในคำสั่ง class หรือ def ก่อนที่จะไปถึงวงในสุด พวกเขาไม่สนใจถ้าด้วยและลองคำสั่ง
ncoghlan

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) เงื่อนไขควรเป็นฟังก์ชั่นที่มีพารามิเตอร์หนึ่งตัวซึ่งควรจะเต็มไปด้วยเนื้อหา (เป็นเหตุผลเดียวที่จะไม่ใช้คลาสสิกขณะวนซ้ำ)


5
การเขียนwhile True: stuff(); if not condition(): breakเป็นความคิดที่ดีมาก ขอบคุณ!
Noctis Skytower

2
@ ZeD ทำไม 1) น่าเกลียด? มันค่อนข้างโอเค IMHO
Sergey Lossev

@SergeyLossev มันจะเป็นเรื่องยากที่จะเข้าใจตรรกะของโปรแกรมเพราะมันจะปรากฏเป็นวงวนไม่สิ้นสุดในตอนแรกถ้าคุณมีโค้ด 'สิ่งของ' จำนวนมากในระหว่างนั้น
exic

16

นี่คือวิธีแก้ปัญหา crazier ของรูปแบบที่แตกต่างกัน - ใช้ coroutines รหัสยังคงคล้ายกันมาก แต่มีความแตกต่างที่สำคัญอย่างหนึ่ง ไม่มีเงื่อนไขการออกเลย! coroutine (chain of coroutines จริงๆ) จะหยุดลงเมื่อคุณหยุดป้อนข้อมูล

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 Skytower

13

วิธีที่ฉันทำสิ่งนี้มีดังนี้ ...

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

นี่ดูเหมือนว่าฉันจะเป็นทางออกที่ง่ายฉันประหลาดใจที่ฉันไม่ได้เห็นที่นี่แล้ว เห็นได้ชัดว่าสิ่งนี้สามารถย้อนกลับไปที่

while not condition:

เป็นต้น


คุณพูดว่า "ฉันแปลกใจที่ฉันไม่ได้เห็นมันมาที่นี่แล้ว" - แต่ฉันไม่เห็นความแตกต่างจากสมมุติว่าโซลูชันของ powderflask จากปี 2010 มันเหมือนกันทุกประการ ("condition = True while condition: # loop body here condition = test_loop_condition () # end of loop")
cslotty

10

สำหรับสิ่งที่ต้องทำขณะที่มีคำสั่ง try

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

หรือเมื่อไม่จำเป็นต้องใช้ประโยค 'ในที่สุด'

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 ที่ดูเหมือนว่าน่าเกลียดกว่าการใช้ตัวแบ่ง
mattdm

5
นั่นเป็นเรื่องที่ฉลาด แต่มันจำเป็นต้องstuffมีฟังก์ชั่นหรือเพื่อให้เนื้อความรหัสซ้ำ
Noctis Skytower

12
สิ่งที่จำเป็นคือwhile condition:เพราะis Trueบอกเป็นนัย ๆ
martineau

2
สิ่งนี้จะล้มเหลวหากconditionขึ้นอยู่กับตัวแปรภายในบางตัวstuff()เพราะตัวแปรนั้นไม่ได้ถูกกำหนดไว้ในขณะนั้น
yo '25

5
ไม่ใช่ตรรกะเดียวกันเนื่องจากในการวนซ้ำครั้งล่าสุดเมื่อเงื่อนไข! = จริง: มันเรียกรหัสในครั้งสุดท้าย ไหนเป็นทำในขณะที่เรียกรหัสหนึ่งครั้งแรกแล้วสภาพการตรวจสอบอีกครั้งก่อนที่จะทำงาน ทำในขณะที่: รันบล็อกหนึ่งครั้ง จากนั้นตรวจสอบและเรียกใช้ซ้ำคำตอบนี้: ตรวจสอบและเรียกใช้ซ้ำ จากนั้นดำเนินการป้องกันรหัสครั้งเดียว แตกต่างใหญ่!
Zv_oDD

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 อีกครั้งดังนั้นฉันจำเป็นต้อง 'ดำเนินการต่อ' โดยไม่ต้องวนซ้ำรายการถัดไป ฉันไม่ทราบวิธีการทำสิ่งดังกล่าวใน 'for s ใน l:' iteration :(. ใน do-while loop, 'ดำเนินการต่อ' จะประเมินรายการปัจจุบันซ้ำอีกครั้งในตอนท้าย
grigoryvp

จากนั้นคุณสามารถกำหนดรหัสหลอกสำหรับเครื่องสถานะของคุณเพื่อให้เราสามารถบอกคุณถึงวิธีแก้ปัญหาแบบไพ ธ อนที่ดีที่สุดได้หรือไม่? ฉันไม่ค่อยรู้เรื่องเครื่องจักรของรัฐ (และอาจไม่ใช่เครื่องเดียว) ดังนั้นถ้าคุณบอกเราเกี่ยวกับอัลกอริทึมของคุณเราจะช่วยคุณได้ง่ายขึ้น
มาร์ติน

สำหรับลูปไม่สามารถใช้งานได้กับสิ่งต่าง ๆ เช่น: a = fun () ในขณะที่ a == 'zxc': sleep (10) a = fun ()
แฮร์รี่

สิ่งนี้ขาดจุดตรวจสอบบูลีนอย่างสมบูรณ์
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- ฉันไม่สามารถอนุมานได้ว่าค่าจริงหรือค่าเท็จหมายถึงอะไร แต่การใช้งานหรือdone ผลัดรหัสลงendOfIteration while not done: ...
IceArdor

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

สำหรับฉันโดยทั่วไปในขณะที่วงจะเป็นดังนี้:

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

ฉันสามารถรวมสำหรับ..ลูปในขณะที่ลูปเช่นกันถ้าสถานการณ์ดังนั้นรับประกันสำหรับลูปผ่านชุดเงื่อนไขอื่น

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.