Python: ดำเนินการต่อในการวนซ้ำถัดไปในวงนอก


136

ฉันอยากรู้ว่ามีวิธีในตัวเพื่อดำเนินการวนซ้ำต่อไปในวงนอกใน python หรือไม่ ตัวอย่างเช่นพิจารณารหัส:

for ii in range(200):
    for jj in range(200, 400):
        ...block0...
        if something:
            continue
    ...block1...

ฉันต้องการให้คำสั่งต่อไปนี้ออกจากลูป jj และไปที่รายการถัดไปในลูป ii ฉันสามารถใช้ตรรกะนี้ด้วยวิธีอื่นได้ (โดยการตั้งค่าตัวแปรแฟล็ก) แต่มีวิธีง่ายๆในการทำเช่นนี้หรือว่าขอมากเกินไป


11
มีจริงแล้วคำสั่งข้ามไปทำงานให้กับงูหลาม: entrian.com/goto มันถูกปล่อยออกมาเป็นเรื่องตลกของ April Fool :-) แต่ควรจะใช้งานได้
codeape

3
โปรดอย่าใช้เรื่องตลกโกโต! มันฉลาดมาก แต่คุณจะเสียใจในภายหลังถ้าใส่ลงในโค้ดของคุณ
Ned Batchelder

คำตอบ:


72
for i in ...:
    for j in ...:
        for k in ...:
            if something:
                # continue loop i

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

ปรับโครงสร้างลูปที่คุณต้องการหลบหนีให้เป็นฟังก์ชัน

def inner():
    for j in ...:
        for k in ...:
            if something:
                return


for i in ...:
    inner()

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

หรือคุณสามารถกำหนดinnerเป็นฟังก์ชันซ้อนกันแล้วปล่อยให้มันจับสิ่งที่ต้องการ (อาจช้ากว่านี้?)

for i in ...:
    def inner():
        for j in ...:
            for k in ...:
                if something:
                    return
    inner()

ใช้ข้อยกเว้น

ตามหลักปรัชญานี่คือข้อยกเว้นสำหรับการทำลายขั้นตอนของโปรแกรมผ่านโครงสร้างการเขียนโปรแกรมที่มีโครงสร้าง (if, for, while) เมื่อจำเป็น

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

สิ่งที่ไม่ดีในแนวทางนี้คือผู้เขียนล่าม / คอมไพเลอร์มักจะคิดว่าข้อยกเว้นพิเศษและปรับให้เหมาะสมสำหรับพวกเขาตามนั้น

class ContinueI(Exception):
    pass


continue_i = ContinueI()

for i in ...:
    try:
        for j in ...:
            for k in ...:
                if something:
                    raise continue_i
    except ContinueI:
        continue

สร้างคลาสข้อยกเว้นพิเศษสำหรับสิ่งนี้เพื่อที่คุณจะได้ไม่เสี่ยงต่อการปิดเสียงข้อยกเว้นอื่น ๆ โดยไม่ได้ตั้งใจ

อย่างอื่นทั้งหมด

ฉันแน่ใจว่ายังมีทางแก้ไขอื่น ๆ


ไม่อยากจะเชื่อเลยว่าฉันไม่คิดจะย้ายวงที่สองไปใช้วิธีอื่น ฉันเริ่มช้า
pmccallum

1
สำหรับฉันแล้วการใช้ข้อยกเว้นเป็นวิธีที่ดีในการบรรลุสิ่งนี้ ฉันเห็นด้วยกับ @ user7610 - "ตามหลักปรัชญานี่คือข้อยกเว้นสำหรับ"
Renato Byrro

ตัวแปรบูลีนเก่าที่ดีและคำสั่ง If?
MrR

OP กำลังมองหาทางเลือกอื่นในการแก้ปัญหา "ฉันสามารถใช้ตรรกะนี้ในทางอื่นได้ (โดยการตั้งค่าตัวแปรค่าสถานะ) [... ]"
user7610

150
for ii in range(200):
    for jj in range(200, 400):
        ...block0...
        if something:
            break
    else:
        ...block1...

Break จะทำลายวงในและจะไม่ดำเนินการ block1 (จะทำงานก็ต่อเมื่อออกจากวงในตามปกติ)


1
สวัสดีมีตัวเลือกอื่น ๆ เช่นนี้หรือไม่? เพราะฉันต้องการทำอีกอันสำหรับลูปใน block1 และรหัสของฉันจะไปลึก 3 ระดับ สถานการณ์แปลก ๆ
สหัส

3
สำหรับฉันดูเหมือนว่าคุณกำลังพยายามทำอะไรบางอย่างกับลูปที่จะเข้าหาได้ดีที่สุดด้วยวิธีที่แตกต่างออกไป ...
Kimvais

ใช่. นั่นเป็นเหตุผลที่ฉันไม่ใช้โครงสร้าง for..else ตอนนี้ฉันยังต้องการลูป แต่ฉันจะใช้ตัวแปรแฟล็กเพื่อเบี่ยงเบนการควบคุม
สหัส

3
for...elseมักจะเป็นโครงสร้างที่มีประโยชน์แม้ว่าอาจทำให้สับสนได้ เพียงจำไว้ว่าelseหมายความว่า "ไม่หยุดพัก" ในบริบทนี้
asmeurer

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

42

ในภาษาอื่นคุณสามารถติดป้ายกำกับลูปและแยกออกจากลูปที่มีป้ายกำกับได้ Python Enhancement Proposal (PEP) 3136 แนะนำให้เพิ่มสิ่งเหล่านี้ลงใน Pythonแต่Guido ปฏิเสธ :

อย่างไรก็ตามฉันปฏิเสธมันเนื่องจากโค้ดที่ซับซ้อนจนต้องใช้คุณสมบัตินี้หายากมาก ในกรณีส่วนใหญ่จะมีวิธีแก้ไขปัญหาที่สร้างรหัสใหม่ทั้งหมดเช่นการใช้ 'return' ในขณะที่ฉันแน่ใจว่ามีกรณีจริง (หายาก) บางกรณีที่ความชัดเจนของรหัสอาจได้รับผลกระทบจากการปรับโครงสร้างใหม่ที่ทำให้สามารถใช้การส่งคืนได้ แต่จะถูกชดเชยด้วยสองประเด็น:

  1. เพิ่มความซับซ้อนให้กับภาษาอย่างถาวร สิ่งนี้ไม่เพียงส่งผลกระทบต่อการใช้งาน Python ทั้งหมดเท่านั้น แต่ยังรวมถึงเครื่องมือวิเคราะห์ซอร์สทุกตัวรวมถึงเอกสารประกอบทั้งหมดสำหรับภาษาด้วย

  2. ความคาดหวังของฉันว่าคุณลักษณะนี้จะถูกใช้ในทางที่ผิดมากกว่าที่จะใช้อย่างถูกต้องทำให้ความชัดเจนของโค้ดลดลงสุทธิ (วัดจากโค้ด Python ทั้งหมดที่เขียนต่อจากนี้) โปรแกรมเมอร์ขี้เกียจมีอยู่ทุกหนทุกแห่งและก่อนที่คุณจะรู้ว่าคุณมีรหัสที่ไม่สามารถเข้าใจได้ในมือของคุณ

ดังนั้นหากนั่นคือสิ่งที่คุณหวังไว้ว่าคุณจะโชคไม่ดี แต่ลองดูคำตอบอื่น ๆ เพราะมีตัวเลือกที่ดีอยู่ที่นั่น


4
น่าสนใจ ฉันเห็นด้วยกับกุยโดที่นี่ แม้ว่ามันจะดีสำหรับบางกรณี แต่มันก็จะถูกทำร้าย อีกเหตุผลหนึ่งที่ไม่นำไปใช้ก็คือตอนนี้พอร์ตโค้ดไปมาระหว่าง C และ Python ค่อนข้างตรงไปตรงมา เมื่อ Python เริ่มรับคุณสมบัติที่ภาษาอื่นขาดสิ่งนี้จะยากขึ้น ยกตัวอย่างเช่นความจริงที่ว่าคุณสามารถมีคำสั่งอื่นสำหรับลูปใน Python ... สิ่งนี้ทำให้โค้ดพกพาไปยังภาษาอื่นได้น้อยลง
eric.frederich

2
ทุกคนทักทาย Guido BDFL ของเรา
JnBrymn

4
นี่เป็นเรื่องน่ากลัวมากกว่าการโต้เถียงที่ดี แต่สำหรับฉันแล้วดูเหมือนว่าพฤติกรรมของfor-elseจะซับซ้อนกว่าอ่านยากกว่าและอาจถูกทารุณกรรมมากกว่า (ถ้าไม่ใช่ความผิดพลาดทันที) มากกว่าลูปที่ตั้งชื่อไว้ ฉันคิดว่าฉันจะใช้คำหลักที่แตกต่างจากelse- บางทีสิ่งที่คิดว่าresumeจะดี? คุณbreakอยู่ในวงและresumeหลังจากนั้นหรือไม่?
ArtOfWarfare

5
สิ่งนี้ทำให้ฉันเศร้า ฉันไม่อยากจะเชื่อเลยว่าฉันรักและเกลียด Python ในเวลาเดียวกันได้อย่างไร สวยมาก แต่ wtf
jlh

5
@jlh ส่วนใหญ่ wtf สำหรับฉัน บางครั้งฉันคิดว่ามันต้องการที่จะแตกต่างไม่ใช่เพื่อวัตถุประสงค์ที่ถูกต้อง แต่เพียงเพื่อให้แตกต่าง นี่เป็นตัวอย่างที่ดีว่า ฉันเจอความต้องการที่จะทำลายลูปด้านนอกค่อนข้างบ่อย
Rikaelus

14

ฉันคิดว่าคุณสามารถทำสิ่งนี้ได้:

for ii in range(200):
    restart = False
    for jj in range(200, 400):
        ...block0...
        if something:
            restart = True
            break
    if restart:
        continue
    ...block1...

4
-1: OP ระบุอย่างชัดเจนว่าพวกเขารู้ว่าพวกเขาสามารถทำอะไรแบบนี้ได้และดูเหมือนว่าคำตอบที่ยอมรับจะเป็นเวอร์ชันที่ยุ่งกว่า (ซึ่งเกิดขึ้นก่อนหน้าของคุณภายใน 8 เดือนดังนั้นจึงเป็นไปไม่ได้ที่คุณจะพลาดการยอมรับ ตอบ).
ArtOfWarfare

10
คำตอบที่ได้รับการยอมรับไม่ชัดเจนถ้าคุณไม่เคยเห็นfor, elseก่อน (และฉันคิดว่าแม้คนส่วนใหญ่ที่มีจำไม่ได้ปิดด้านบนของหัวของพวกเขาวิธีการทำงาน)
asmeurer

3

ฉันคิดว่าวิธีที่ง่ายที่สุดวิธีหนึ่งในการบรรลุเป้าหมายนี้คือการแทนที่ "ดำเนินการต่อ" ด้วยคำสั่ง "หยุดพัก" กล่าวคือ

for ii in range(200):
 for jj in range(200, 400):
    ...block0...
    if something:
        break
 ...block1...       

ตัวอย่างเช่นนี่คือรหัสง่าย ๆ เพื่อดูว่ามันดำเนินไปอย่างไร:

for i in range(10):
    print("doing outer loop")
    print("i=",i)
    for p in range(10):
        print("doing inner loop")
        print("p=",p)
        if p==3:
            print("breaking from inner loop")
            break
    print("doing some code in outer loop")

2

อีกวิธีในการจัดการกับปัญหาประเภทนี้คือการใช้ Exception ()

for ii in range(200):
    try:
        for jj in range(200, 400):
            ...block0...
            if something:
                raise Exception()
    except Exception:
        continue
    ...block1...

ตัวอย่างเช่น:

for n in range(1,4):
    for m in range(1,4):
        print n,'-',m

ผลลัพธ์:

    1-1
    1-2
    1-3
    2-1
    2-2
    2-3
    3-1
    3-2
    3-3

สมมติว่าเราต้องการข้ามไปยังวงนอก n จากลูป m ถ้า m = 3:

for n in range(1,4):
    try:
        for m in range(1,4):
            if m == 3:
                raise Exception()            
            print n,'-',m
    except Exception:
        continue

ผลลัพธ์:

    1-1
    1-2
    2-1
    2-2
    3-1
    3-2

ลิงค์อ้างอิง: http://www.programming-idioms.org/idiom/42/continue-outer-loop/1264/python


1

เราต้องการค้นหาบางสิ่งบางอย่างแล้วหยุดการวนซ้ำภายใน ฉันใช้ระบบแฟล็ก

for l in f:
    flag = True
    for e in r:
        if flag==False:continue
        if somecondition:
            do_something()
            flag=False

ฉันไม่รู้ว่าทำไมโซลูชันของคุณจึงถูกลดลง มีคนโพสต์โดยทั่วไปเหมือนกันทุก
ประการ

ฉันไม่ค่อยโชคดีกับ stackoverflow
Esther

1
อาจเป็นเพราะโดยพื้นฐานแล้วมีสิ่งเดียวกันที่โพสต์ไว้ที่นี่อยู่แล้วฉันเดาว่า ... และFalse:continueสิ่งนั้นก็คือ ... ตามที่มักเกิดขึ้นในระบบ "ธรรมชาติ" ที่เลขชี้กำลังเป็นบรรทัดฐานคุณจะต้องโชคดีเพียงไม่กี่ครั้งใน SO เพื่อสะสมคะแนนชื่อเสียงจำนวนมาก อย่างไรก็ตามคำตอบที่ "ดีที่สุด" ของฉันมักเป็นคำตอบที่ได้รับความนิยมน้อยที่สุด
user7610

0

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

for ii in range(200):
    done = any([op(ii, jj) for jj in range(200, 400)])
    ...block0...
    if done:
        continue
    ...block1...

โดยที่ op คือตัวดำเนินการบูลีนบางตัวที่ทำหน้าที่ร่วมกันระหว่าง ii และ jj ในกรณีของฉันหากการดำเนินการใด ๆ กลับมาเป็นจริงแสดงว่าฉันทำเสร็จแล้ว

สิ่งนี้ไม่ได้แตกต่างจากการแตกโค้ดออกเป็นฟังก์ชัน แต่ฉันคิดว่าการใช้ตัวดำเนินการ "ใด ๆ " เพื่อทำตรรกะหรือในรายการบูลีนและการทำตรรกะทั้งหมดในบรรทัดเดียวนั้นน่าสนใจ นอกจากนี้ยังหลีกเลี่ยงการเรียกใช้ฟังก์ชัน

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