Python: Tiếp tục lặp lại tiếp theo trong vòng lặp bên ngoài


135

Tôi muốn biết nếu có bất kỳ cách tích hợp nào để tiếp tục lặp lại tiếp theo trong vòng lặp bên ngoài trong python. Ví dụ, hãy xem xét mã:

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

Tôi muốn câu lệnh tiếp tục này thoát khỏi vòng lặp jj và goto mục tiếp theo trong vòng lặp ii. Tôi có thể thực hiện logic này theo một số cách khác (bằng cách đặt biến cờ), nhưng có một cách dễ dàng để làm điều này, hoặc điều này giống như yêu cầu quá nhiều?


11
Thực sự tồn tại một câu lệnh goto hoạt động cho Python: entrian.com/goto . Nó được phát hành như một trò đùa của kẻ ngốc tháng tư :-), nhưng được cho là có tác dụng.
codeape

3
Oh, xin đừng sử dụng trò đùa goto đó! Nó rất thông minh, nhưng bạn sẽ buồn sau này nếu bạn đặt nó vào mã của mình.
Ned Batchelder

Câu trả lời:


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

Trong trường hợp chung, khi bạn có nhiều cấp độ lặp và breakkhông hoạt động cho bạn (vì bạn muốn tiếp tục một trong các vòng trên, không phải ở cấp trên bên trên hiện tại), bạn có thể thực hiện một trong các cách sau

Tái cấu trúc các vòng lặp bạn muốn thoát khỏi thành một hàm

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


for i in ...:
    inner()

Nhược điểm là bạn có thể cần chuyển đến hàm mới đó một số biến, trước đây là trong phạm vi. Bạn có thể chuyển chúng dưới dạng tham số, biến chúng thành các biến đối tượng trên một đối tượng (tạo một đối tượng mới chỉ cho hàm này, nếu nó có ý nghĩa) hoặc biến toàn cục, singletons, bất cứ điều gì (ehm, ehm).

Hoặc bạn có thể định nghĩa innerlà một hàm lồng nhau và để nó chỉ nắm bắt những gì nó cần (có thể chậm hơn?)

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

Sử dụng ngoại lệ

Về mặt triết học, đây là những gì ngoại lệ dành cho, phá vỡ dòng chương trình thông qua các khối xây dựng lập trình có cấu trúc (nếu, trong, trong khi) khi cần thiết.

Ưu điểm là bạn không phải chia một đoạn mã thành nhiều phần. Điều này là tốt nếu nó là một loại tính toán mà bạn đang thiết kế trong khi viết nó bằng Python. Giới thiệu trừu tượng ở điểm đầu này có thể làm bạn chậm lại.

Điều tồi tệ với cách tiếp cận này là các tác giả phiên dịch / biên dịch thường cho rằng các ngoại lệ là đặc biệt và tối ưu hóa cho chúng theo đó.

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

Tạo một lớp ngoại lệ đặc biệt cho việc này, để bạn không có nguy cơ vô tình làm im lặng một số ngoại lệ khác.

Một cái gì đó khác hoàn toàn

Tôi chắc chắn vẫn còn những giải pháp khác.


Không thể tin rằng tôi đã không nghĩ đến việc chuyển vòng lặp thứ hai của mình sang một phương pháp khác. Tôi đang trở nên chậm chạp
pmccallum

1
Đối với tôi, sử dụng Ngoại lệ là một cách tốt để đạt được điều này. Tôi đồng ý với @ user7610 - "về mặt triết học, đây là trường hợp ngoại lệ dành cho".
Renato Byrro

Biến boolean cũ tốt và câu lệnh If?
MrR

OP đang tìm kiếm một giải pháp thay thế cho điều đó, "Tôi có thể triển khai logic này theo một cách khác (bằng cách đặt biến cờ) [...]"
user7610

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

Break sẽ phá vỡ vòng lặp bên trong và block1 sẽ không được thực thi (nó sẽ chỉ chạy nếu vòng lặp bên trong được thoát bình thường).


1
Xin chào, có lựa chọn nào khác như thế này không? Bởi vì tôi muốn thực hiện một vòng lặp khác trong block1 và như thế, mã của tôi sẽ đi sâu 3 cấp. Tình hình kỳ lạ.
Sahas

3
Đối với tôi điều này nghe có vẻ như bạn đang cố gắng làm điều gì đó với các vòng lặp sẽ được tiếp cận tốt nhất theo một cách khác ...
Kimvais

Đúng. Đó là lý do tại sao tôi không sử dụng cấu trúc for..else. Bây giờ tôi vẫn cần các vòng lặp, nhưng tôi sẽ sử dụng các biến cờ để chuyển hướng điều khiển.
Sahas

3
for...elsethường là một cấu trúc hữu ích, mặc dù nó có thể gây nhầm lẫn. Chỉ cần nhớ điều đó elsecó nghĩa là "không nghỉ ngơi" trong bối cảnh này.
asmeker

Điều này dường như được giới hạn chỉ trong hai lớp vòng. Tôi cần cập nhật một số mã có ba vòng lặp lồng nhau và một yêu cầu mới của khách hàng có nghĩa là trong một số trường hợp nhất định, vòng lặp trong cùng cần tiếp tục lặp lại vòng lặp ngoài cùng tiếp theo. Tôi cho rằng đề nghị của bạn sẽ không áp dụng cho kịch bản đó.
kasperd

42

Trong các ngôn ngữ khác, bạn có thể gắn nhãn cho vòng lặp và thoát khỏi vòng lặp được gắn nhãn. Đề xuất cải tiến Python (PEP) 3136 đã đề xuất thêm chúng vào Python nhưng Guido đã từ chối nó :

Tuy nhiên, tôi đang từ chối nó trên cơ sở mã quá phức tạp để yêu cầu tính năng này là rất hiếm. Trong hầu hết các trường hợp, có các công việc hiện tại tạo ra mã sạch, ví dụ như sử dụng 'return'. Mặc dù tôi chắc chắn có một số trường hợp thực tế (hiếm) trong đó sự rõ ràng của mã sẽ bị tái cấu trúc khiến cho nó có thể sử dụng trả về, nhưng điều này được bù đắp bởi hai vấn đề:

  1. Sự phức tạp thêm vào ngôn ngữ, vĩnh viễn. Điều này ảnh hưởng đến không chỉ tất cả các triển khai Python, mà còn mọi công cụ phân tích nguồn, cộng với tất nhiên là tất cả tài liệu cho ngôn ngữ.

  2. Tôi kỳ vọng rằng tính năng này sẽ bị lạm dụng nhiều hơn so với tính năng này sẽ được sử dụng đúng, dẫn đến giảm độ rõ ràng của mã (được đo trên tất cả các mã Python được viết từ đó). Các lập trình viên lười biếng ở khắp mọi nơi, và trước khi bạn biết điều đó, bạn có một mớ hỗn độn đáng kinh ngạc trên tay mã không thể hiểu được.

Vì vậy, nếu đó là những gì bạn đã hy vọng cho bạn hết may mắn, nhưng hãy nhìn vào một trong những câu trả lời khác vì có những lựa chọn tốt ở đó.


4
Hấp dẫn. Tôi đồng ý với Guido ở đây. Trong khi nó sẽ tốt cho một số trường hợp nó sẽ bị lạm dụng. Một lý do khác cho việc không triển khai nó là ngay bây giờ, nó khá dễ dàng để chuyển mã qua lại giữa C và Python. Khi Python bắt đầu chọn các tính năng mà các ngôn ngữ khác thiếu, điều này trở nên khó khăn hơn. Lấy ví dụ thực tế là bạn có thể có một câu lệnh khác trên vòng lặp for trong Python ... điều này làm cho mã ít bị dịch chuyển sang các ngôn ngữ khác.
eric.frederich

2
Tất cả mưa đá Guido BDFL của chúng tôi
JnBrymn

4
Đây là một vụ giết người đỏ hơn là một đối số tốt, nhưng đối với tôi, hành vi của for-elsenó phức tạp hơn, khó đọc hơn và có lẽ bị lạm dụng nhiều hơn (nếu không phải là một lỗi hoàn toàn) so với các vòng lặp được đặt tên. Tôi nghĩ rằng tôi đã sử dụng một từ khóa khác hơn else- có lẽ một cái gì đó như thế resumesẽ tốt? Bạn breaktrong vòng lặp và resumelà ngay sau nó?
ArtOfWarfare

5
Điều này làm cho tôi buồn. Tôi không thể tin rằng tôi yêu và ghét Python cùng một lúc. Thật đẹp, vậy mà wtf.
jlh

5
@jlh Chủ yếu là wtf cho tôi. Đôi khi tôi nghĩ nó muốn khác biệt không phải vì mục đích chính đáng mà chỉ là khác biệt. Đây là một ví dụ tốt về điều đó. Tôi đi qua nhu cầu phá vỡ các vòng lặp bên ngoài khá thường xuyên.
Rikaelus

14

Tôi nghĩ bạn có thể làm một cái gì đó như thế này:

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 tuyên bố rõ ràng rằng họ biết rằng họ có thể làm điều gì đó như thế này và điều này trông giống như một phiên bản lộn xộn hơn của câu trả lời được chấp nhận (trước 8 tháng, vì vậy bạn không thể bỏ lỡ câu trả lời được chấp nhận câu trả lời).
ArtOfWarfare

10
Câu trả lời được chấp nhận là không rõ ràng nếu bạn chưa từng thấy for, elsetrước đó (và tôi nghĩ thậm chí hầu hết những người đã không thể nhớ ra khỏi đỉnh đầu của họ như thế nào nó hoạt động).
asmeker

3

Tôi nghĩ một trong những cách dễ nhất để đạt được điều này là thay thế câu lệnh "tiếp tục" bằng câu lệnh "break", tức là

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

Ví dụ, đây là mã dễ dàng để xem chính xác nó diễn ra như thế nào:

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

Một cách khác để giải quyết loại vấn đề này là sử dụng Exception ().

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

Ví dụ:

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

kết quả:

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

Giả sử chúng ta muốn nhảy đến vòng lặp n bên ngoài từ vòng lặp m nếu 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

kết quả:

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

Liên kết tham khảo: http://www.programming-idioms.org/idiom/42/continue-outer-loop/1264/python


1

Chúng tôi muốn tìm một cái gì đó và sau đó dừng lặp lại bên trong. Tôi sử dụng một hệ thống cờ.

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

Tôi không biết lý do tại sao giải pháp của bạn bị hạ cấp; về cơ bản ai đó đã đăng chính xác điều tương tự và đã được nâng cấp 10 lần
Locane

Tôi không may mắn lắm với stackoverflow.
Esther

1
Có lẽ bởi vì về cơ bản đã có cùng một thứ được đăng ở đây, tôi đoán ... Và False:continueđiều này là ... định dạng bất thường. Như thường thấy trong các hệ thống "tự nhiên" trong đó số mũ là tiêu chuẩn, bạn chỉ cần gặp may mắn một vài lần trên SO để tích lũy được số điểm danh tiếng đáng kể. Dù sao, câu trả lời "tốt nhất" của tôi thường là những câu trả lời ít phổ biến nhất.
dùng7610

0

Tôi chỉ làm một cái gì đó như thế này. Giải pháp của tôi cho việc này là thay thế vòng lặp bên trong bằng một sự hiểu biết danh sách.

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

trong đó op là một số toán tử boolean hoạt động trên sự kết hợp của ii và jj. Trong trường hợp của tôi, nếu bất kỳ thao tác nào trả về đúng, tôi đã hoàn thành.

Điều này thực sự không khác biệt gì so với việc phá mã thành một hàm, nhưng tôi nghĩ rằng việc sử dụng toán tử "any" để thực hiện logic OR trong danh sách các booleans và thực hiện logic tất cả trong một dòng là điều thú vị. Nó cũng tránh cuộc gọi chức năng.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.