วิธีการแบ่งออกเป็นหลายลูป


480

รับรหัสต่อไปนี้ (ที่ใช้งานไม่ได้):

while True:
    #snip: print out current state
    while True:
        ok = get_input("Is this ok? (y/n)")
        if ok.lower() == "y": break 2 #this doesn't work :(
        if ok.lower() == "n": break
    #do more processing with menus and stuff

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


87
ทำไมงูหลามจึงไม่มี 'break (n)' โดยที่ n คือจำนวนของระดับที่คุณต้องการแยกออก
นาธาน

2
C ++ ดีมากgotoถ้าคุณอยู่ลึกเข้าไปในลูปมากมาย
Drake Johnson

คำตอบ:


512

สัญชาตญาณแรกของฉันคือการ refactor วนซ้อนกันเป็นฟังก์ชั่นและใช้returnในการสลาย


3
นี่เป็นความคิดอื่นที่ฉันมีเนื่องจากฟังก์ชั่น get_input_yn () จะมีประโยชน์ในที่อื่นด้วยเช่นกันฉันแน่ใจ
Matthew Scharley

96
เห็นด้วยในกรณีเฉพาะนี้ แต่ในกรณีทั่วไปของ 'ฉันมีลูปซ้อนกันฉันควรทำอย่างไร' การปรับโครงสร้างใหม่อาจไม่สมเหตุสมผล
quick_dry

การใช้ข้อยกเว้นอาจง่ายขึ้นเมื่อคุณต้องให้ผลตอบแทนแทนที่จะใช้การส่งคืนอย่างไรก็ตามคุณควรใช้ itertools.islice () ในกรณีเช่นนี้
เบิร์ตคิง

5
มันเป็นไปได้ที่จะ refactor ห่วงภายในเป็นวิธีการของตัวเองที่ส่งกลับจริงเพื่อดำเนินการต่อเท็จเพื่อทำลายวงรอบนอก ในขณะที่ condition1: / ถ้าไม่ใช่ MyLoop2 (params): break อีกทางเลือกหนึ่งคือการตั้งค่าสถานะบูลีนซึ่งทดสอบทั้งสองระดับ more = True / while condition1 และอื่น ๆ : / while condition2 และอื่น ๆ : / ถ้า stopCondition: more = False / break / ...
ToolmakerSteve

7
ฉันยอมรับว่าการพยายามใช้งานreturnเป็นวิธีที่เหมาะสม และเหตุผลก็คือตามเซนของ Python "แบนดีกว่าซ้อนกัน" เรามีการทำรังสามระดับที่นี่และถ้านั่นเริ่มต้นในทางมันเป็นเวลาที่จะลดการทำรังหรืออย่างน้อยแยกรังทั้งในฟังก์ชั่นของตัวเอง
Lutz Prechelt

240

นี่เป็นอีกวิธีหนึ่งที่สั้น ข้อเสียคือคุณสามารถทำลายวงรอบนอกเท่านั้น แต่บางครั้งก็เป็นสิ่งที่คุณต้องการ

for a in xrange(10):
    for b in xrange(20):
        if something(a, b):
            # Break the inner loop...
            break
    else:
        # Continue if the inner loop wasn't broken.
        continue
    # Inner loop was broken, break the outer.
    break

สิ่งนี้ใช้โครงสร้าง for / else ที่อธิบายไว้ที่: ทำไมไพ ธ อนจึงใช้ 'else' หลังจาก for และ while loops?

ข้อมูลเชิงลึกที่สำคัญ: ดูเหมือนว่าลูปด้านนอกจะแตกเสมอ แต่ถ้าวงด้านในไม่แตกออกมาวงนอกก็ไม่เหมือนกัน

continueคำสั่งคือความมหัศจรรย์ที่นี่ มันอยู่ในประโยคอื่น ตามคำนิยามที่เกิดขึ้นหากไม่มีการแบ่งด้านใน ในสถานการณ์นั้นcontinueหลีกเลี่ยงการแบ่งด้านนอกอย่างเรียบร้อย


6
@eugeney ทำไมล่ะ ตัวแบ่งแรกจะออกจากวงด้านใน
Navin

5
@eugeney ฉันรู้สึกว่าฉันขาดอะไรบางอย่างที่นี่ คุณสามารถโพสต์ตัวอย่างได้ไหม?
Navin

4
@ Mingliang ที่สามารถไปก่อนดำเนินการต่อ
Baldrickk

1
รับสิ่งนี้จากวิดีโอของ Raymond Hettinger youtu.be/OSGv2VnC0go?t=971อ่านคำสั่ง "else" ที่แนบมากับลูปเป็น "no_break" จากนั้นจะเข้าใจได้ง่ายขึ้น
Ambareesh

2
นี่คือฉลาด :-) อย่างไรก็ตามไม่ตรงไปข้างหน้า ตรงไปตรงมาฉันไม่เชื่อว่ามีการขัดแย้งเพื่อให้หยุดพักหรือหยุด (n) จากงูใหญ่ วิธีแก้ไขปัญหาเพิ่มความซับซ้อนมากขึ้น
rfportilla

148

PEP 3136เสนอตัวแบ่ง / ติดป้ายกำกับ กุยโดปฏิเสธเพราะ "รหัสที่ซับซ้อนซึ่งต้องใช้คุณสมบัตินี้หายากมาก" PEP ได้กล่าวถึงวิธีแก้ปัญหาบางอย่างแม้ว่า (เช่นเทคนิคการยกเว้น) ในขณะที่ Guido รู้สึกว่าการปรับโครงสร้างการใช้งานคืนจะง่ายกว่าในกรณีส่วนใหญ่


73
แม้ว่า refactor / returnมักจะเป็นวิธีที่จะไปฉันได้เห็นบางกรณีที่break 2คำสั่งสั้น ๆ ง่ายๆ ' ' จะทำให้รู้สึกมาก นอกจากนี้ refactor / ไม่ทำงานเหมือนกันสำหรับreturn continueในกรณีเหล่านี้การแบ่งตัวเลขและการดำเนินการต่อจะง่ายกว่าในการติดตามและทำให้เกิดความยุ่งเหยิงน้อยกว่าการปรับโครงสร้างให้เป็นฟังก์ชั่นเล็ก ๆ ยกระดับข้อยกเว้นหรือตรรกะที่ซับซ้อนที่เกี่ยวข้องกับการตั้งค่าสถานะ มันเป็นความอัปยศ Guido ปฏิเสธมัน
James Haigh

10
break; breakจะดี.
PyRulez

5
@ Jeyekomon ปัญหาคือคุณไม่จำเป็นต้องมีลูปซ้อนกัน 3 ตัวขึ้นไปเพื่อให้เป็นปัญหา ลูปซ้อนกัน 2 วงเป็นเรื่องธรรมดามาก
จอน

6
"รหัสที่ซับซ้อนมากที่ต้องใช้คุณสมบัตินี้หายากมาก" แต่ถ้าคุณใช้รหัสที่ซับซ้อนนี้การขาดลูปที่ติดฉลากจะทำให้มันซับซ้อนยิ่งขึ้นเนื่องจากคุณต้องส่งต่อbreakลูปทั้งหมดด้วยตนเอง โง่.
BallpointBen

3
เห็นได้ชัดว่าฉันสามารถแก้ไขโพสต์เป็นเวลา 5 นาทีเท่านั้น (เป็น 6) ดังนั้นนี่คือโพสต์ที่แก้ไขของฉัน: 2 cents ของฉัน: Perl มีป้ายกำกับ break (แต่เรียกมันว่า 'ล่าสุด') และ 'ถัดไป' เพื่อดำเนินการต่อไปยังการทำซ้ำครั้งถัดไปโดยตรง มันไม่ได้หายากเลย - ฉันใช้มันตลอดเวลา ฉันเป็นแบรนด์ใหม่ของ Python และมีความต้องการอยู่แล้ว นอกจากนี้การแบ่งหมายเลขจะน่ากลัวสำหรับการปรับโครงสร้าง - ดีกว่าการติดป้ายกำกับวนซ้ำที่คุณต้องการแยกออกจากนั้นใช้ตัวแบ่ง <label> เพื่อระบุอย่างชัดเจนว่าวงใดที่คุณต้องการแยกออกจากกัน
John Deighan

119

ประการแรกตรรกะสามัญมีประโยชน์

หากด้วยเหตุผลบางอย่างเงื่อนไขการยกเลิกไม่สามารถใช้งานได้ข้อยกเว้นเป็นแผนสำรอง

class GetOutOfLoop( Exception ):
    pass

try:
    done= False
    while not done:
        isok= False
        while not (done or isok):
            ok = get_input("Is this ok? (y/n)")
            if ok in ("y", "Y") or ok in ("n", "N") : 
                done= True # probably better
                raise GetOutOfLoop
        # other stuff
except GetOutOfLoop:
    pass

สำหรับตัวอย่างเฉพาะนี้ข้อยกเว้นอาจไม่จำเป็น

ในทางกลับกันเรามักจะมีตัวเลือก "Y", "N" และ "Q" ในแอปพลิเคชันโหมดอักขระ สำหรับตัวเลือก "Q" เราต้องการทางออกทันที มันยอดเยี่ยมกว่ามาก


4
อย่างจริงจังยกเว้นมีราคาถูกมากและงูหลามใช้สำนวนมากมาย มันง่ายมากที่จะกำหนดและโยนของที่กำหนดเองเช่นกัน
Gregg Lind

13
ความคิดที่น่าสนใจ ฉันฉีกขาดว่าจะรักหรือเกลียดมัน
Craig McQueen

8
โซลูชันนี้จะมีประโยชน์มากขึ้นหากพบว่ามีสองรูปแบบแยกกัน (1) ใช้ธง ( done) (2) ยกข้อยกเว้น การรวมเข้าด้วยกันเป็นโซลูชันเดียวทำให้มันดูซับซ้อน สำหรับผู้อ่านในอนาคต: EITHER ใช้บรรทัดทั้งหมดที่เกี่ยวข้องdoneหรือกำหนดGetOutOfLoop(Exception)และเพิ่ม / ยกเว้น
ToolmakerSteve

4
โดยทั่วไปแล้วการใช้การลองบล็อกเพื่อสิ่งอื่นนอกจากนั้นข้อยกเว้นจะขมวดคิ้วอย่างมาก Try-blocks ได้รับการออกแบบมาโดยเฉพาะสำหรับการจัดการข้อผิดพลาดและการใช้มันสำหรับการควบคุมการไหลที่แปลกไม่ดีนักโวหาร
nobillygreen

3
@ tommy.carstensen นั่นเป็นเรื่องไร้สาระ; ทั้งการกำหนด subclass ข้อยกเว้นใหม่และเพิ่มมัน (ดังที่แสดงในคำตอบ) และส่งข้อความที่กำหนดเองไปยังExceptionนวกรรมิก (เช่นraise Exception('bla bla bla')) นั้นใช้ได้ทั้ง Python 2 และ Python 3 อดีตเป็นที่นิยมในกรณีนี้เพราะเราไม่ต้องการexceptบล็อกของเราจับข้อยกเว้นทั้งหมดแต่มีข้อยกเว้นพิเศษที่เราใช้เพื่อออกจากลูป หากเราทำสิ่งต่าง ๆ ตามที่คุณแนะนำและข้อผิดพลาดในรหัสของเราทำให้เกิดข้อยกเว้นที่ไม่คาดคิดขึ้นมันจะได้รับการปฏิบัติอย่างผิดพลาดเหมือนกับการจงใจออกจากลูป
Mark Amery

54

ฉันมักจะเห็นด้วยว่าการปรับโครงสร้างในฟังก์ชั่นมักเป็นวิธีที่ดีที่สุดสำหรับสถานการณ์แบบนี้ แต่เมื่อคุณต้องการแบ่งลูปซ้อนกันจริงๆนี่เป็นตัวแปรที่น่าสนใจของวิธีการยกข้อยกเว้นที่ @ S.Lott อธิบาย มันใช้withคำสั่งPython เพื่อสร้างข้อยกเว้นให้ดูดีขึ้นเล็กน้อย กำหนดผู้จัดการบริบทใหม่ (คุณต้องทำสิ่งนี้เพียงครั้งเดียว) ด้วย:

from contextlib import contextmanager
@contextmanager
def nested_break():
    class NestedBreakException(Exception):
        pass
    try:
        yield NestedBreakException
    except NestedBreakException:
        pass

ตอนนี้คุณสามารถใช้ตัวจัดการบริบทนี้ดังนี้:

with nested_break() as mylabel:
    while True:
        print "current state"
        while True:
            ok = raw_input("Is this ok? (y/n)")
            if ok == "y" or ok == "Y": raise mylabel
            if ok == "n" or ok == "N": break
        print "more processing"

ข้อดี: (1) มันสะอาดกว่าเล็กน้อย (ไม่มีบล็อกลองยกเว้นชัดเจน) และ (2) คุณจะได้Exceptionคลาสย่อยที่สร้างขึ้นเองสำหรับการใช้งานแต่ละครั้งnested_break; ไม่จำเป็นต้องประกาศExceptionคลาสย่อยของคุณเองทุกครั้ง


40

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

คุณสามารถใช้gotoด้วยวิธีต่อไปนี้ (โดยใช้โมดูล April Fools จากที่นี่ ):

#import the stuff
from goto import goto, label

while True:
    #snip: print out current state
    while True:
        ok = get_input("Is this ok? (y/n)")
        if ok == "y" or ok == "Y": goto .breakall
        if ok == "n" or ok == "N": break
    #do more processing with menus and stuff
label .breakall

ฉันรู้ฉันรู้ว่า "เจ้าจะไม่ใช้ goto" และทั้งหมดนั้น แต่มันใช้ได้ดีในกรณีแปลก ๆ เช่นนี้


1
ถ้ามันเป็นเหมือนคำสั่ง COME FROM ใน INTERCAL ก็ไม่มีอะไร
1800 ข้อมูล

3
ฉันชอบเรื่องตลก แต่จุดล้นของกองคือการส่งเสริมรหัสที่ดีดังนั้นฉันต้องลงคะแนนให้คุณ :(
คริสเตียน Oudard

13
ฉันคิดว่ามันเป็นวิธีแก้ปัญหาที่สะอาดและสามารถอ่านได้เพื่อให้มีคุณสมบัติเป็นโค้ดที่ดี :)
JT Hurley

1
@JTHurley ไม่มีสิ่งนี้ไม่สะอาดและอ่านง่าย ฉันหมายความว่ามันอาจจะมีลักษณะเช่นนั้นมีความสะอาดและสามารถอ่านได้ในตัวอย่างนี้แต่ในสถานการณ์ชีวิตจริงใด ๆ ที่ข้ามไปมาสร้างระเบียบศักดิ์สิทธิ์ (นอกจากนี้ยังเป็นยาต่อต้าน pythonic ... )
Alois Mahdal

2
กลับไปได้รับตัวแทนที่ไม่ดีนักเขียนมืออาชีพใด ๆ ควรจะสามารถจัดการกับมันได้อย่างถูกต้อง
Albert Renshaw

33

แนะนำตัวแปรใหม่ที่คุณจะใช้เป็น 'ตัวแบ่งลูป' ขั้นแรกให้กำหนดบางสิ่งให้กับมัน (False, 0, ฯลฯ ) จากนั้นในวงนอกรอบนอกก่อนที่คุณจะแยกมันให้เปลี่ยนค่าเป็นอย่างอื่น (True, 1, ... ) เมื่อออกจากวงให้ทำการวนรอบ 'แม่' ตรวจสอบค่านั้น ให้ฉันสาธิต:

breaker = False #our mighty loop exiter!
while True:
    while True:
        if conditionMet:
            #insert code here...
            breaker = True 
            break
    if breaker: # the interesting part!
        break   # <--- !

หากคุณมีการวนซ้ำไม่สิ้นสุดนี่เป็นทางออกเดียวเท่านั้น สำหรับการประมวลผลลูปอื่น ๆ นั้นเร็วกว่ามาก วิธีนี้ใช้งานได้หากคุณมีลูปซ้อนกันหลายลูป คุณสามารถออกทั้งหมดหรือเพียงไม่กี่ ความเป็นไปได้ไม่สิ้นสุด! หวังว่านี่จะช่วยได้!


22

หากต้องการแยกลูปซ้อนหลายวงโดยไม่ต้องเปลี่ยนโครงสร้างเป็นฟังก์ชันให้ใช้คำสั่ง "จำลองคำสั่ง goto" พร้อมกับข้อยกเว้น StopIterationในตัว :

try:
    for outer in range(100):
        for inner in range(100):
            if break_early():
                raise StopIteration

except StopIteration: pass

ดูการสนทนานี้เกี่ยวกับการใช้คำสั่ง goto เพื่อแยกลูปซ้อนกันออก


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

ในความเป็นจริง StopIteration ใช้สำหรับเครื่องกำเนิดไฟฟ้า แต่ฉันคิดว่าปกติคุณไม่มีข้อยกเว้น StopIteration ดังนั้นจึงดูเหมือนเป็นโซลูชันที่ดี แต่ไม่มีข้อผิดพลาดในการสร้างข้อยกเว้นใหม่อย่างไรก็ตาม
สกี้

1
ทางออกที่ดีที่สุดและง่ายที่สุดสำหรับฉัน
Alexandre Huat

16

keeplooping=True
while keeplooping:
    #Do Stuff
    while keeplooping:
          #do some other stuff
          if finisheddoingstuff(): keeplooping=False

หรืออะไรทำนองนั้น คุณสามารถตั้งค่าตัวแปรในลูปด้านในและตรวจสอบในลูปด้านนอกทันทีหลังจากที่ลูปด้านในออกจากการแตกถ้าเหมาะสม ฉันชอบวิธี GOTO ถ้าคุณไม่รังเกียจที่จะใช้โมดูลตลกของ April Fool - ไม่ใช่ Pythonic แต่มันก็สมเหตุสมผล


นี่คือการตั้งค่าธง!
SIslam

ฉันคิดว่ามันเป็นทางออกที่ดีมาก
สกี้

13

นี่ไม่ใช่วิธีที่ดีที่สุดที่จะทำ แต่ในความคิดของฉันมันเป็นวิธีที่ดีที่สุด

def loop():
    while True:
    #snip: print out current state
        while True:
            ok = get_input("Is this ok? (y/n)")
            if ok == "y" or ok == "Y": return
            if ok == "n" or ok == "N": break
        #do more processing with menus and stuff

ฉันค่อนข้างมั่นใจว่าคุณสามารถหาสิ่งที่ใช้การสอบถามซ้ำได้ที่นี่เช่นกัน แต่ฉันก็ไม่รู้ว่ามันเป็นตัวเลือกที่ดีสำหรับคุณหรือไม่


นี่เป็นทางออกที่เหมาะสมสำหรับฉัน กรณีการใช้งานของฉันแตกต่างจาก OP อย่างมาก ฉันวนลูปมากกว่าข้อมูลเดิมสองเท่าเพื่อหาการเรียงสับเปลี่ยนดังนั้นฉันไม่ต้องการแยกทั้งสองออกจากกันในขณะที่ลูป
Brian Peterson

9

และทำไมไม่ลองวนซ้ำถ้าสองเงื่อนไขเป็นจริง ฉันคิดว่านี่เป็นวิธี pythonic เพิ่มเติม:

dejaVu = True

while dejaVu:
    while True:
        ok = raw_input("Is this ok? (y/n)")
        if ok == "y" or ok == "Y" or ok == "n" or ok == "N":
            dejaVu = False
            break

ไม่ใช่เหรอ

ทั้งหมดที่ดีที่สุด


ทำไมไม่เพียงwhile dejaVu:? คุณตั้งค่าเป็นจริงอยู่ดี
Matthew Scharley

เฮ้ที่ใช้งานได้! ฉันคิดในสองTrueเงื่อนไขที่จะข้ามสองลูป แต่เพียงหนึ่งก็เพียงพอแล้ว
Mauro Aspé

2
@ MatewScharley ฉันคิดว่านี่คือการแสดงให้เห็นว่างานนี้ในลูปซ้อนกัน
จัดการ

@ MauroAspéสิ่งนี้จะไม่ทำสิ่งที่ OP ร้องขอ มันจะยังคงดำเนินการวนรอบนอกทั้งหมด แต่เป้าหมายคือถ้าคุณทำลายส่วนที่เหลือของรหัสจะไม่ได้รับการประหารชีวิต
yamm

@yamm ไม่สามารถแก้ไขด้วยif not dejaVu: breakด้านล่างและออกจากวงหลักได้หรือไม่ ฉันคิดว่าวิธีแก้ปัญหานั้นใกล้เคียงที่สุดกับสิ่งที่ถูกถาม +1
milcak

8

ใช้ตรรกะลูปของคุณเป็นตัววนซ้ำที่ให้ผลตอบแทนตัวแปรลูปและส่งคืนเมื่อเสร็จแล้ว - นี่คือวิธรรมดาที่วางรูปภาพในแถว / คอลัมน์จนกว่าเราจะออกจากภาพหรือนอกสถานที่

def it(rows, cols, images):
    i = 0
    for r in xrange(rows):
        for c in xrange(cols):
            if i >= len(images):
                return
            yield r, c, images[i]
            i += 1 

for r, c, image in it(rows=4, cols=4, images=['a.jpg', 'b.jpg', 'c.jpg']):
    ... do something with r, c, image ...

นี่คือข้อดีของการแยกลอจิกวงที่ซับซ้อนและการประมวลผล ...


3

ในกรณีนี้ตามที่คนอื่นชี้ให้เห็นเช่นกันการสลายตัวของการทำงานเป็นวิธีที่จะไป รหัสใน Python 3:

def user_confirms():
    while True:
        answer = input("Is this OK? (y/n) ").strip().lower()
        if answer in "yn":
            return answer == "y"

def main():
    while True:
        # do stuff
        if user_confirms():
            break

3

มีเคล็ดลับที่ซ่อนอยู่ในwhile ... elseโครงสร้างPython ซึ่งสามารถใช้ในการจำลองการแบ่งคู่โดยไม่มีการเปลี่ยนแปลง / เพิ่มเติมรหัสมาก ในสาระสำคัญหากwhileเงื่อนไขเป็นเท็จelseบล็อกจะถูกเรียก ไม่มีข้อยกเว้นcontinueหรือbreakเรียกใช้elseบล็อก สำหรับข้อมูลเพิ่มเติมโปรดดูที่คำตอบ " ข้ออื่นในหลามในขณะที่คำสั่ง " หรืองูหลาม doc ในขณะที่ (v2.7)

while True:
    #snip: print out current state
    ok = ""
    while ok != "y" and ok != "n":
        ok = get_input("Is this ok? (y/n)")
        if ok == "n" or ok == "N":
            break    # Breaks out of inner loop, skipping else

    else:
        break        # Breaks out of outer loop

    #do more processing with menus and stuff

ข้อเสียเพียงอย่างเดียวคือคุณต้องย้ายเงื่อนไขการแบ่งคู่ออกเป็นwhileเงื่อนไข (หรือเพิ่มตัวแปรแฟล็ก) การเปลี่ยนแปลงนี้มีอยู่สำหรับforลูปที่elseบล็อกถูกเรียกใช้หลังจากการวนซ้ำเสร็จสิ้น


ดูเหมือนจะไม่เป็นไปตามข้อกำหนดของการพักสองครั้ง ใช้งานได้สำหรับปัญหาที่ระบุ แต่ไม่ใช่สำหรับคำถามจริง
Dakkaron

@Dakkaron คุณแน่ใจว่าคุณเข้าใจรหัสถูกต้องหรือไม่ รหัสนี้แก้ปัญหา OPs ได้อย่างแน่นอนและมีความคล้ายคลึงกับคำขอ อย่างไรก็ตามมันไม่ได้แบ่งออกเป็นหลายวง แต่ใช้ประโยคอื่นเพื่อแทนที่ความต้องการในการเพิ่มตัวแบ่งสองเท่า
holroy

จากความเข้าใจของฉันคำถามคือHow to break out of multiple loops in Python?และคำตอบควรได้รับ "มันไม่ทำงานลองอย่างอื่น" ฉันรู้ว่ามันแก้ไขตัวอย่างที่ได้รับจาก OP แต่จะไม่ตอบคำถามของพวกเขา
Dakkaron

@ Dakkaron, ดูคำชี้แจงปัญหาภายใต้รหัสและในความคิดของฉันมันแน่นอนตอบคำถาม OPs
holroy

2

อีกวิธีในการลดการวนซ้ำของคุณเป็นลูประดับเดียวก็คือการใช้เครื่องกำเนิดไฟฟ้าตามที่ระบุไว้ในการอ้างอิงหลาม

for i, j in ((i, j) for i in A for j in B):
    print(i , j)
    if (some_condition):
        break

คุณสามารถปรับขนาดได้ตามระดับของลูป

ข้อเสียคือคุณไม่สามารถทำลายเพียงระดับเดียวอีกต่อไป มันคือทั้งหมดหรือไม่มีอะไร

ข้อเสียอีกอย่างคือมันไม่สามารถทำงานได้ในขณะที่ลูป ฉันต้องการโพสต์คำตอบนี้ไว้บนPythonแต่เดิม`` แบ่งออกจากลูปทั้งหมดแต่น่าเสียดายที่ปิดเป็นซ้ำของอันนี้


1
มันทำงานได้ในขณะที่ลูปเกินไปคุณจะต้องเขียนตัวสร้างของคุณเป็น def (พร้อมผลตอบแทน) ไม่เป็นความเข้าใจ
Veky

ใช่ผู้พูดที่ PyCon อ้างว่าที่นี่แม้คำตอบที่ได้รับการยอมรับของ @RobertRossney ก็ไม่ได้เป็น Pythonic อย่างแท้จริง แต่เครื่องกำเนิดไฟฟ้าเป็นวิธีที่เหมาะสมในการทำลายหลายลูป (ฉันขอแนะนำให้ดูวิดีโอทั้งหมด!)
Post169

2

เหตุผลที่ฉันมาที่นี่ก็เพราะฉันมีวงนอกและวงในเช่น:

for x in array:
  for y in dont_use_these_values:
    if x.value==y:
      array.remove(x)  # fixed, was array.pop(x) in my original answer
      continue

  do some other stuff with x

อย่างที่คุณเห็นมันจะไม่ไปที่ x ต่อไป แต่จะไปที่ y ต่อไปแทน

สิ่งที่ฉันพบเพื่อแก้ปัญหานี้ก็คือการทำงานผ่านอาร์เรย์สองครั้งแทน

for x in array:
  for y in dont_use_these_values:
    if x.value==y:
      array.remove(x)  # fixed, was array.pop(x) in my original answer
      continue

for x in array:
  do some other stuff with x

ฉันรู้ว่านี่เป็นกรณีเฉพาะของคำถามของ OP แต่ฉันโพสต์ด้วยความหวังว่ามันจะช่วยให้ใครบางคนคิดเกี่ยวกับปัญหาของพวกเขาแตกต่างกันในขณะที่ทำให้สิ่งต่าง ๆ เรียบง่าย


นี่อาจจะไม่ใช่ Python ประเภทของอาร์เรย์คืออะไร? อาจเป็นรายการ แต่มันมีอะไร? แม้ว่ามันจะมี ints, array.pop (x) อาจจะไม่ทำสิ่งที่คุณต้องการ
Veky

นั่นเป็นจุดที่ดี ฉันหารหัสที่ฉันอ้างอิงไม่พบ สำหรับทุกคนที่อ่านข้อความนี้ array.pop (i) "ลบรายการที่มีดัชนี i ออกจากอาร์เรย์และส่งคืน" ตามเอกสารหลาม ดังนั้นหนึ่งจะต้องได้รับดัชนีของรายการ x ในอาร์เรย์เพื่อให้รหัสนี้ทำงานตามที่คาดไว้ นอกจากนี้ยังมีฟังก์ชัน array.remove (x) ซึ่งจะทำสิ่งที่คาดหวัง ฉันจะแก้ไขคำตอบของฉันด้านบนเพื่อแก้ไขข้อผิดพลาดนั้น สิ่งนี้จะถือว่าอาร์เรย์ที่สองไม่มีการซ้ำซ้อนเนื่องจาก array.remove (x) จะลบอินสแตนซ์แรกของ x ที่พบเท่านั้น
Nathan Garabedian

ตกลงแล้วฉันจะได้รับมัน ในกรณีนั้นเพียงแค่ใช้breakแทนที่จะcontinueทำในสิ่งที่คุณต้องการใช่ไหม? :-)
Veky

ใช่เพื่อประสิทธิภาพและความชัดเจนคุณอาจต้องการใช้ตัวแบ่งแทนดำเนินการต่อในตัวอย่างเหล่านี้ :)
นาธานการาเบเดียน

2

ลองใช้เครื่องกำเนิดไฟฟ้าที่ไม่มีที่สิ้นสุด

from itertools import repeat
inputs = (get_input("Is this ok? (y/n)") for _ in repeat(None))
response = (i.lower()=="y" for i in inputs if i.lower() in ("y", "n"))

while True:
    #snip: print out current state
    if next(response):
        break
    #do more processing with menus and stuff

2

โดยใช้ฟังก์ชั่น:

def myloop():
    for i in range(1,6,1):  # 1st loop
        print('i:',i)
        for j in range(1,11,2):  # 2nd loop
            print('   i, j:' ,i, j)
            for k in range(1,21,4):  # 3rd loop
                print('      i,j,k:', i,j,k)
                if i%3==0 and j%3==0 and k%3==0:
                    return  # getting out of all loops

myloop()

ลองใช้รหัสข้างต้นโดยแสดงความคิดเห็นด้วยreturnเช่นกัน

โดยไม่ต้องใช้ฟังก์ชั่นใด ๆ :

done = False
for i in range(1,6,1):  # 1st loop
    print('i:', i)
    for j in range(1,11,2):  # 2nd loop
        print('   i, j:' ,i, j)
        for k in range(1,21,4):  # 3rd loop
            print('      i,j,k:', i,j,k)
            if i%3==0 and j%3==0 and k%3==0:
                done = True
                break  # breaking from 3rd loop
        if done: break # breaking from 2nd loop
    if done: break     # breaking from 1st loop

ตอนนี้ให้เรียกใช้โค้ดด้านบนตามที่เป็นอยู่ก่อนแล้วลองเรียกใช้โดยใส่breakเครื่องหมายความคิดเห็นแต่ละบรรทัดที่มีหนึ่งบรรทัดจากด้านล่าง


2

วิธีง่ายๆในการเปลี่ยนลูปหลาย ๆ อันให้กลายเป็นลูปเดียวที่สามารถย่อยสลายได้คือการใช้ numpy.ndindex

for i in range(n):
  for j in range(n):
    val = x[i, j]
    break # still inside the outer loop!

for i, j in np.ndindex(n, n):
  val = x[i, j]
  break # you left the only loop there was!

คุณต้องจัดทำดัชนีลงในวัตถุของคุณซึ่งแตกต่างจากความสามารถในการวนซ้ำผ่านค่าต่าง ๆ อย่างชัดเจน แต่อย่างน้อยในกรณีง่าย ๆ ดูเหมือนว่าจะง่ายกว่าประมาณ 2-20 เท่าของคำตอบส่วนใหญ่ที่แนะนำ


2
# this version uses a level counter to choose how far to break out

break_levels = 0
while True:
    # snip: print out current state
    while True:
        ok = get_input("Is this ok? (y/n)")
        if ok == "y" or ok == "Y":
            break_levels = 1        # how far nested, excluding this break
            break
        if ok == "n" or ok == "N":
            break                   # normal break
    if break_levels:
        break_levels -= 1
        break                       # pop another level
if break_levels:
    break_levels -= 1
    break

# ...and so on

1

อาจเป็นกลอุบายเล็ก ๆ น้อย ๆ เช่นด้านล่างนี้หากไม่ต้องการปรับโครงสร้างให้ทำงาน

เพิ่ม 1 break_level variable เพื่อควบคุมเงื่อนไข while loop

break_level = 0
# while break_level < 3: # if we have another level of nested loop here
while break_level < 2:
    #snip: print out current state
    while break_level < 1:
        ok = get_input("Is this ok? (y/n)")
        if ok == "y" or ok == "Y": break_level = 2 # break 2 level
        if ok == "n" or ok == "N": break_level = 1 # break 1 level

1

คุณสามารถกำหนดตัวแปร (ตัวอย่างเช่นbreak_statement ) จากนั้นเปลี่ยนเป็นค่าที่แตกต่างกันเมื่อเกิดเงื่อนไขสองจุดและใช้ในถ้าคำสั่งแตกจากลูปที่สองด้วย

while True:
    break_statement=0
    while True:
        ok = raw_input("Is this ok? (y/n)")
        if ok == "n" or ok == "N": 
            break
        if ok == "y" or ok == "Y": 
            break_statement=1
            break
    if break_statement==1:
        break

จุดดีอย่างไรก็ตามในแต่ละระดับเหนือระดับความสนใจภายในของเราเราจะต้องสแกนตัวแปรนั้น รู้สึกไม่ดีจริง ๆ ว่าภาษานั้นไม่มีคำสั่ง GoTo ที่ชาญฉลาด
Anatoly Alekseev

1

ฉันอยากจะเตือนคุณว่าฟังก์ชั่นใน Python สามารถสร้างได้ตรงกลางโค้ดและสามารถเข้าถึงตัวแปรที่อยู่รอบ ๆ ได้อย่างโปร่งใสสำหรับการอ่านและด้วยnonlocalหรือglobalเพื่อประกาศสำหรับการเขียน

ดังนั้นคุณสามารถใช้ฟังก์ชั่นเป็น "โครงสร้างการควบคุมแบบเปราะบาง" ซึ่งกำหนดสถานที่ที่คุณต้องการกลับไปที่:

def is_prime(number):

    foo = bar = number

    def return_here():
        nonlocal foo, bar
        init_bar = bar
        while foo > 0:
            bar = init_bar
            while bar >= foo:
                if foo*bar == number:
                    return
                bar -= 1
            foo -= 1

    return_here()

    if foo == 1:
        print(number, 'is prime')
    else:
        print(number, '=', bar, '*', foo)

>>> is_prime(67)
67 is prime
>>> is_prime(117)
117 = 13 * 9
>>> is_prime(16)
16 = 4 * 4

1

วิธีแก้ปัญหาใน 2 วิธี

ด้วยตัวอย่าง: เมทริกซ์สองตัวนี้มีค่าเท่ากันหรือไม่
matrix1 และ matrix2 มีขนาดเท่ากัน, n, 2 เมทริกซ์เชิงมิติ

โซลูชั่นแรก , โดยไม่ต้องฟังก์ชั่น

same_matrices = True
inner_loop_broken_once = False
n = len(matrix1)

for i in range(n):
    for j in range(n):

        if matrix1[i][j] != matrix2[i][j]:
            same_matrices = False
            inner_loop_broken_once = True
            break

    if inner_loop_broken_once:
        break

โซลูชั่นที่สอง , มีฟังก์ชั่น
นี้เป็นทางออกสุดท้ายสำหรับกรณีของฉัน

def are_two_matrices_the_same (matrix1, matrix2):
    n = len(matrix1)
    for i in range(n):
        for j in range(n):
            if matrix1[i][j] != matrix2[i][j]:
                return False
    return True

ขอให้มีความสุขมาก ๆ ในวันนี้!


1
# this version breaks up to a certain label

break_label = None
while True:
    # snip: print out current state
    while True:
        ok = get_input("Is this ok? (y/n)")
        if ok == "y" or ok == "Y":
            break_label = "outer"   # specify label to break to
            break
        if ok == "n" or ok == "N":
            break
    if break_label:
        if break_label != "inner":
            break                   # propagate up
        break_label = None          # we have arrived!
if break_label:
    if break_label != "outer":
        break                       # propagate up
    break_label = None              # we have arrived!

#do more processing with menus and stuff

0

หวังว่านี่จะช่วย:

x = True
y = True
while x == True:
    while y == True:
         ok = get_input("Is this ok? (y/n)") 
         if ok == "y" or ok == "Y":
             x,y = False,False #breaks from both loops
         if ok == "n" or ok == "N": 
             break #breaks from just one

0

นี่คือการใช้งานที่ดูเหมือนจะใช้งานได้:

break_ = False
for i in range(10):
    if break_:
        break
    for j in range(10):
        if j == 3:
            break_ = True
            break
        else:
            print(i, j)

การดึงกลับอย่างเดียวคือคุณต้องกำหนดbreak_ก่อนลูป


0

ไม่มีวิธีการทำเช่นนี้จากระดับภาษา บางภาษามี goto ที่คนอื่นมีตัวแบ่งที่ใช้อาร์กิวเมนต์, หลามไม่

ตัวเลือกที่ดีที่สุดคือ:

  1. ตั้งค่าสถานะซึ่งตรวจสอบโดยลูปภายนอกหรือตั้งค่าเงื่อนไขลูปภายนอก

  2. ใส่วนในฟังก์ชันและใช้ return เพื่อแยกลูปทั้งหมดในครั้งเดียว

  3. จัดระเบียบตรรกะของคุณใหม่

เครดิตไปที่ Vivek Nagarajan โปรแกรมเมอร์ตั้งแต่ปี 1987


การใช้ฟังก์ชั่น

def doMywork(data):
    for i in data:
       for e in i:
         return 

ใช้ธง

is_break = False
for i in data:
   if is_break:
      break # outer loop break
   for e in i:
      is_break = True
      break # inner loop break

-3

คล้ายกับรุ่นก่อน แต่มีขนาดกะทัดรัดกว่า (Booleans เป็นเพียงตัวเลข)

breaker = False #our mighty loop exiter!
while True:
    while True:
        ok = get_input("Is this ok? (y/n)")
        breaker+= (ok.lower() == "y")
        break

    if breaker: # the interesting part!
        break   # <--- !

2
มันดูน่าเกลียดและทำให้โค้ดยากที่จะเข้าใจเมื่อเทียบกับโค้ดก่อนหน้า นอกจากนี้มันผิด มันพลาดการตรวจสอบจริง ๆ ว่าอินพุตเป็นที่ยอมรับและแบ่งหลังจาก 1 ลูป
Eric

-3

Exceptionเนื่องจากคำถามนี้ได้กลายเป็นคำถามมาตรฐานสำหรับการทำลายเป็นห่วงโดยเฉพาะอย่างยิ่งผมอยากจะให้คำตอบของฉันกับตัวอย่างการใช้

แม้ว่าจะไม่มีชื่อฉลากว่าแบ่งเป็นลูปในโครงสร้างวนลูปแบบทวีคูณ แต่เราสามารถใช้ข้อยกเว้นที่ผู้ใช้กำหนดเองเพื่อเจาะเข้าไปในลูปเฉพาะของตัวเลือกของเรา ลองพิจารณาตัวอย่างต่อไปนี้ที่ให้เราพิมพ์ตัวเลขทั้งหมดไม่เกิน 4 หลักในระบบเลขฐาน 6

class BreakLoop(Exception):
    def __init__(self, counter):
        Exception.__init__(self, 'Exception 1')
        self.counter = counter

for counter1 in range(6):   # Make it 1000
    try:
        thousand = counter1 * 1000
        for counter2 in range(6):  # Make it 100
            try:
                hundred = counter2 * 100
                for counter3 in range(6): # Make it 10
                    try:
                        ten = counter3 * 10
                        for counter4 in range(6):
                            try:
                                unit = counter4
                                value = thousand + hundred + ten + unit
                                if unit == 4 :
                                    raise BreakLoop(4) # Don't break from loop
                                if ten == 30: 
                                    raise BreakLoop(3) # Break into loop 3
                                if hundred == 500:
                                    raise BreakLoop(2) # Break into loop 2
                                if thousand == 2000:
                                    raise BreakLoop(1) # Break into loop 1

                                print('{:04d}'.format(value))
                            except BreakLoop as bl:
                                if bl.counter != 4:
                                    raise bl
                    except BreakLoop as bl:
                        if bl.counter != 3:
                            raise bl
            except BreakLoop as bl:
                if bl.counter != 2:
                    raise bl
    except BreakLoop as bl:
        pass

เมื่อเราพิมพ์ผลลัพธ์เราจะไม่ได้รับค่าใด ๆ ที่มีหน่วยเป็น 4 ในกรณีนี้เราไม่แยกจากลูปใด ๆ ตามที่BreakLoop(4)ยกขึ้นและติดในลูปเดียวกัน ในทำนองเดียวกันเมื่อใดก็ตามที่สิบสถานที่จะมี 3 BreakLoop(3)เราบุกเข้าไปในวงที่สามใช้ เมื่อใดก็ตามที่ร้อยที่มี 5 เราแบ่งออกเป็นสองวงโดยใช้BreakLoop(2)และเมื่อใดก็ตามที่หนึ่งพันมี 2 เราก็แบ่งออกเป็นวงแรกโดยใช้BreakLoop(1)เราบุกเข้าไปในวงครั้งแรกที่ใช้

กล่าวโดยสรุปยกข้อยกเว้นของคุณ (ในตัวหรือสร้างโดยผู้ใช้กำหนด) ในวงในและจับมันในวงจากที่คุณต้องการกลับมาควบคุมของคุณ หากคุณต้องการแยกจากลูปทั้งหมดให้จับข้อยกเว้นภายนอกลูปทั้งหมด (ฉันไม่ได้แสดงกรณีนี้ในตัวอย่าง)

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