Jak wyjść z wielu pętli?


479

Biorąc pod uwagę następujący kod (który nie działa):

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

Czy istnieje sposób, aby to zadziałało? Czy też muszę wykonać jedną kontrolę, aby wyjść z pętli wejściowej, a następnie inną, bardziej ograniczoną, sprawdzić w pętli zewnętrznej, aby zerwać wszystko razem, jeśli użytkownik jest zadowolony?


87
Dlaczego Python nie ma po prostu „break (n)”, gdzie n jest liczbą poziomów, z których chcesz się wydostać.
Nathan

2
C ++ jest fajny, gotojeśli jesteś głęboko osadzony w wielu pętlach
Drake Johnson

Odpowiedzi:


512

Moim pierwszym instynktem byłoby przekształcenie zagnieżdżonej pętli w funkcję i użycie jej returndo wybicia.


3
To kolejna myśl, którą miałem, ponieważ funkcja get_input_yn () byłaby przydatna także w innych miejscach, jestem pewien.
Matthew Scharley,

96
uzgodniono w tym konkretnym przypadku, ale w ogólnym przypadku „Mam zagnieżdżone pętle, co mam zrobić” refaktoryzacja może nie mieć sensu.
quick_dry 10.10.08

użycie wyjątku może być łatwiejsze, gdy musisz ustąpić zamiast używać return, jednak prawdopodobnie w takim przypadku powinieneś użyć itertools.islice ().
Robert King

5
Zazwyczaj możliwe jest przeformułowanie wewnętrznej pętli na jej własną metodę, która zwraca true, aby kontynuować, false, aby przerwać zewnętrzną pętlę. while condition1: / if not MyLoop2 (params): break. Alternatywą jest ustawienie flagi logicznej, która jest testowana na obu poziomach. more = True / while condition1 i więcej: / while condition2 i więcej: / if stopCondition: more = False / break / ...
ToolmakerSteve

7
Zgadzam się, że dążenie do korzystania returnjest właściwym podejściem. Powodem jest to, że według Zen Pythona „mieszkanie jest lepsze niż zagnieżdżone”. Mamy tutaj trzy poziomy zagnieżdżania i jeśli to zacznie przeszkadzać, nadszedł czas, aby zmniejszyć zagnieżdżenie lub przynajmniej wyodrębnić całe zagnieżdżenie do własnej funkcji.
Lutz Prechelt,

239

Oto inne podejście, które jest krótkie. Wadą jest to, że możesz przełamać tylko zewnętrzną pętlę, ale czasami jest to dokładnie to, czego chcesz.

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

Wykorzystuje konstrukcję for / else wyjaśnioną na stronie: Dlaczego Python używa pętli „else” po pętli for i while?

Kluczowy wgląd: Wydaje się , że zewnętrzna pętla zawsze pęka.Ale jeśli pętla wewnętrzna nie pęknie, pętla zewnętrzna też nie.

continueStwierdzenie jest magia tutaj. Jest w klauzuli for-else. Z definicji dzieje się tak, jeśli nie ma wewnętrznej przerwy. W tej sytuacji continuestarannie omija zewnętrzną przerwę.


6
@eugeney Dlaczego nie? Pierwsza przerwa wydostanie się z wewnętrznej pętli.
Navin

5
@eugeney Mam wrażenie, że coś mi brakuje. Czy możesz opublikować przykład?
Navin

4
@Mingliang, które można przejść przed kontynuowaniem.
Baldrickk,

1
Otrzymałem to z filmu Raymonda Hettingera, youtu.be/OSGv2VnC0go?t=971 , przeczytaj instrukcje „else” dołączone do pętli jako „no_break”, a następnie łatwiej to zrozumieć.
Ambareesh

2
To jest sprytne. :-) Jednak nie jest to proste. Szczerze mówiąc, nie przekonują mnie argumenty, aby utrzymać etykietę break lub break (n) poza Pythonem. Rozwiązania te zwiększają złożoność.
rfportilla

148

PEP 3136 proponuje oznaczenie przerwa / kontynuacja. Guido odrzucił go, ponieważ „tak skomplikowany kod wymagający tej funkcji jest bardzo rzadki”. PEP wspomina jednak o niektórych obejściach (takich jak technika wyjątków), podczas gdy Guido uważa, że ​​refaktoryzacja użycia return będzie w większości przypadków prostsza.


73
Chociaż refaktorem / returnjest zwykle droga, widziałem całkiem sporo przypadków, w których proste zwięzłe break 2stwierdzenie miałoby taki sens. Ponadto refactor / returnnie działa tak samo dla continue. W takich przypadkach numeryczne dzielenie i kontynuowanie byłoby łatwiejsze do naśladowania i mniej zaśmiecone niż refaktoryzacja do małej funkcji, zgłaszanie wyjątków lub zawiłej logiki polegającej na ustawianiu flagi do rozbicia na każdym poziomie gniazda. Szkoda, że ​​Guido go odrzucił.
James Haigh,

10
break; breakbyłoby miło.
PyRulez

5
@Jeyekomon Problem polega na tym, że nie potrzebujesz 3 lub więcej zagnieżdżonych pętli, aby był to problem. 2 zagnieżdżone pętle są dość powszechne
Jon

6
„Kod tak skomplikowany wymagający tej funkcji jest bardzo rzadki”. Ale jeśli kiedykolwiek użyjesz tak skomplikowanego kodu, brak oznaczonych pętli sprawi, że będzie jeszcze bardziej skomplikowany, ponieważ musisz ręcznie przekierować breakwszystkie pętle. Głupi.
BallpointBen

3
Najwyraźniej mogę edytować post tylko przez 5 minut (minęło 6). Oto mój zredagowany post: Moje 2 centy: Perl oznaczył przerwę (ale nazywa ją „ostatnią”) i „następną”, aby przejść bezpośrednio do następnej iteracji. To wcale nie jest rzadkie - używam go cały czas. Jestem zupełnie nowy w Pythonie i już go potrzebuję. Ponadto ponumerowane przerwy byłyby straszne przy refaktoryzacji - lepiej oznaczyć pętlę, z której chcesz się zerwać, a następnie użyj break <label>, aby wyraźnie określić, z której pętli chcesz się zerwać.
John Deighan

119

Po pierwsze, pomocna jest zwykła logika.

Jeśli z jakiegoś powodu warunki zakończenia nie mogą zostać wypracowane, wyjątki stanowią plan awaryjny.

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

W tym konkretnym przykładzie wyjątek może nie być konieczny.

Z drugiej strony często mamy opcje „Y”, „N” i „Q” w aplikacjach w trybie znakowym. W przypadku opcji „Q” chcemy natychmiastowego wyjścia. To bardziej wyjątkowe.


4
Poważnie, wyjątki są wyjątkowo tanie, a idiomatyczny python używa ich bardzo dużo. Bardzo łatwo jest również zdefiniować i wyrzucić niestandardowe.
Gregg Lind,

13
Ciekawy pomysł. Jestem rozdarty, czy to kochać, czy nienawidzić.
Craig McQueen

8
To rozwiązanie byłoby bardziej pomocne, gdyby pokazało dwie odmiany osobno. (1) przy użyciu flagi ( done). (2) zgłaszanie wyjątku. Połączenie ich w jedno rozwiązanie sprawia, że ​​wydaje się to skomplikowane. Dla przyszłych czytelników: UŻYWAJ wszystkich wierszy obejmujących doneLUB zdefiniuj GetOutOfLoop(Exception)i podbij / z wyjątkiem tego.
ToolmakerSteve

4
Ogólnie rzecz biorąc, używanie bloków try do czegokolwiek innego niż wyjątki jest bardzo niezadowolone. Try-bloki są specjalnie zaprojektowane do obsługi błędów, a użycie ich do dziwnego przepływu sterowania nie jest zbyt dobre, stylistycznie.
nobillygreen

2
W Python3 byłoby to raise Exception('GetOutOfLoop')i except Exception:.
tommy.carstensen

54

Zwykle zgadzam się, że refaktoryzacja do funkcji jest zwykle najlepszym podejściem w tego rodzaju sytuacjach, ale gdy naprawdę potrzebujesz wyrwać się z zagnieżdżonych pętli, oto ciekawy wariant podejścia do zgłaszania wyjątków opisanego przez @ S.Lott. Korzysta z withinstrukcji Pythona, aby wyjątek wyglądał nieco ładniej. Zdefiniuj nowego menedżera kontekstu (musisz to zrobić tylko raz) za pomocą:

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

Teraz możesz użyć tego menedżera kontekstu w następujący sposób:

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"

Zalety: (1) jest nieco czystszy (brak wyraźnego bloku try-oprócz), oraz (2) otrzymujesz niestandardową Exceptionpodklasę dla każdego zastosowania nested_break; nie trzeba za Exceptionkażdym razem deklarować własnej podklasy.


40

Po pierwsze, możesz również rozważyć uczynienie procesu pobierania i sprawdzania poprawności danych wejściowych funkcją; w ramach tej funkcji, można po prostu zwrócić wartość, jeżeli jego poprawne, i się obracać w czasie pętli czy nie. To zasadniczo eliminuje rozwiązany problem i zwykle można go zastosować w bardziej ogólnym przypadku (zerwanie wielu pętli). Jeśli absolutnie musisz zachować tę strukturę w swoim kodzie i naprawdę nie chcesz zajmować się rachunkami logicznymi ...

Możesz także użyć goto w następujący sposób (używając modułu April Fools stąd ):

#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

Wiem, wiem, „nie będziesz używać goto” i tak dalej, ale działa to dobrze w takich dziwnych przypadkach.


1
Jeśli jest coś podobnego do komendy COME FROM w INTERCAL, to nic
1800 INFORMACJE

3
lubię żart, ale celem przepełnienia stosu jest promowanie dobrego kodu, więc muszę zagłosować w dół :(
Christian Oudard

13
Myślę, że jest to wystarczająco czyste i czytelne rozwiązanie, aby zakwalifikować się jako dobry kod, dlatego głosuję. :)
JT Hurley

1
@JTHurley nie to nie jest czyste i czytelne. Mam na myśli, że w tym przykładzie może to wyglądać na czyste i czytelne, ale w każdym scenariuszu z prawdziwego życia musisz stworzyć święty bałagan . (Również to jest takie anty-pytoniczne ...)
Alois Mahdal

2
goto dostaje złego przedstawiciela, moim zdaniem każdy profesjonalny programista powinien sobie z tym poradzić.
Albert Renshaw,

33

Wprowadź nową zmienną, której będziesz używać jako „przerywacza pętli”. Najpierw przypisz coś do niego (False, 0 itd.), A następnie w zewnętrznej pętli, zanim się od niego oderwiesz, zmień wartość na coś innego (True, 1, ...). Po wyjściu z pętli sprawdź w pętli „macierzystej” tę wartość. Pokażę:

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

Jeśli masz nieskończoną pętlę, jest to jedyne wyjście; dla innych pętli wykonywanie jest naprawdę dużo szybsze. Działa to również, jeśli masz wiele zagnieżdżonych pętli. Możesz wyjść ze wszystkich lub tylko kilku. Niekończące się możliwości! Mam nadzieję, że to pomogło!


22

Aby wyrwać się z wielu zagnieżdżonych pętli, bez refaktoryzacji do funkcji, skorzystaj z „symulowanej instrukcji goto” z wbudowanym wyjątkiem StopIteration :

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

except StopIteration: pass

Zobacz tę dyskusję na temat używania instrukcji goto do przełamywania zagnieżdżonych pętli.


1
Wygląda to o wiele ładniej niż tworzenie własnej klasy do obsługi wyjątku i wygląda bardzo czysto. Czy jest jakiś powód, dla którego nie powinienem tego robić?
mgjk

W rzeczywistości StopIteration używa generatorów, ale myślę, że normalnie nie masz żadnego niezakończonego wyjątku StopIteration. Wydaje się więc, że to dobre rozwiązanie, ale i tak nie ma błędu przy tworzeniu nowego wyjątku.
Kowalski

1
Najlepsze i najprostsze rozwiązanie dla mnie
Alexandre Huat

16

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

czy jakoś tak. Możesz ustawić zmienną w wewnętrznej pętli i sprawdzić ją w zewnętrznej pętli natychmiast po wyjściu z wewnętrznej pętli, przerywając w razie potrzeby. Podoba mi się metoda GOTO, pod warunkiem, że nie masz nic przeciwko użyciu modułu żartów prima aprilis - nie jest to Python, ale ma sens.


jest to rodzaj ustawienia flagi!
SIslam,

Myślę, że to bardzo dobre rozwiązanie.
Kowalski

13

To nie jest najładniejszy sposób na zrobienie tego, ale moim zdaniem jest to najlepszy sposób.

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

Jestem prawie pewien, że mógłbyś tutaj również opracować coś za pomocą rekurencji, ale nie wiem, czy to dla ciebie dobra opcja.


To było dla mnie właściwe rozwiązanie. Mój przypadek użycia był bardzo różny od PO. Zapętlałem dwa razy zasadniczo te same dane, aby znaleźć permutacje, więc nie chciałem rozdzielać dwóch pętli while.
Brian Peterson,

9

A dlaczego nie kontynuować pętli, jeśli dwa warunki są spełnione? Myślę, że jest to bardziej pythonowy sposób:

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

Czyż nie

Wszystkiego najlepszego.


dlaczego nie while dejaVu:? Zresztą ustawiłeś to na prawdę.
Matthew Scharley,

hej, to działa! Myślałem w dwóch Truewarunkach, aby pominąć dwie pętle, ale wystarczy jedna.
Mauro Aspé,

2
@MatthewScharley Myślę, że to ma pokazać, że działa to w zagnieżdżonych pętlach.
uchwyt

@ MauroAspé nie dokona dokładnie tego, czego wymaga OP. nadal będzie wykonywać całą zewnętrzną pętlę, ale celem jest to, że jeśli złamiesz, reszta kodu nie zostanie wykonana
yam

@yamm Czy nie można tego rozwiązać za pomocą znaku „ if not dejaVu: breaku dołu” i wyjść z głównej pętli? Myślę, że rozwiązanie jest najbliższe pytaniu. +1
milcak

8

Uwzględnij logikę pętli w iteratorze, który zwraca zmienne pętli i zwraca po zakończeniu - oto prosty układ, który układa obrazy w wierszach / kolumnach, dopóki nie zabraknie obrazów lub miejsc, aby je umieścić:

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 ...

Ma to tę zaletę, że dzieli skomplikowaną logikę pętli i przetwarzanie ...


3

W tym przypadku, jak zauważyli również inni, właściwym rozwiązaniem jest rozkład funkcjonalny. Kod w Pythonie 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

W while ... elsestrukturze Pythona istnieje ukryta sztuczka , której można użyć do symulacji podwójnego podziału bez większych zmian / dodatków kodu. Zasadniczo, jeśli whilewarunek jest fałszywy, elseblok jest wyzwalany. Ani wyjątki, continueani breakwyzwalanie elsebloku. Aby uzyskać więcej informacji, zobacz odpowiedzi na „ Else klauzula dotycząca instrukcji Python while ” lub dokumentacja Python podczas korzystania (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

Jedynym minusem jest to, że musisz przenieść warunek podwójnego łamania do whilewarunku (lub dodać zmienną flagową). Istnieją tego warianty dla forpętli, w której elseblok jest wyzwalany po zakończeniu pętli.


To nie wydaje się spełniać wymogu podwójnych przerw. Działa dla dokładnie określonego problemu, ale nie dla rzeczywistego pytania.
Dakkaron

@Dakkaron Czy na pewno dobrze zrozumiałeś kod? Kod rzeczywiście rozwiązuje pytanie OP i łamie się podobnie jak żądanie. Nie wyłamuje się jednak z wielu pętli, ale używa klauzuli else, aby zastąpić potrzebę podwojenia przerwy.
holroy

Z mojego zrozumienia pytanie brzmiało How to break out of multiple loops in Python?i odpowiedź powinna brzmieć: „To nie działa, spróbuj czegoś innego”. Wiem, że to naprawia dokładnie podany przykład PO, ale nie odpowiada na ich pytanie.
Dakkaron,

@Dakkaron, zobacz opis problemu pod kodem i moim zdaniem rzeczywiście odpowiada on na pytanie PO.
holroy

2

Innym sposobem zredukowania iteracji do pętli jednopoziomowej byłoby użycie generatorów, jak również określono w odwołaniu do pytona

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

Możesz skalować go do dowolnej liczby poziomów dla pętli

Minusem jest to, że nie można już przekroczyć tylko jednego poziomu. To wszystko albo nic.

Kolejną wadą jest to, że nie działa z pętlą while. Pierwotnie chciałem opublikować tę odpowiedź na Pythonie - `wyrwij się ze wszystkich pętli, ale niestety jest to zamknięte jako duplikat tego


1
Działa również w pętlach while, wystarczy napisać generator jako def (z wydajnością), a nie jako zrozumienie.
Veky

Tak, prelegent na PyCon twierdzi tutaj, że nawet zaakceptowana odpowiedź @ RobertRossney nie jest prawdziwie pytoniczna, ale generator jest właściwym sposobem na zerwanie wielu pętli. (Polecam obejrzeć cały film!)
Post169

2

Powodem mojego przyjazdu jest to, że miałem zewnętrzną i wewnętrzną pętlę:

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

Jak widać, tak naprawdę nie przejdzie do następnego x, ale zamiast tego przejdzie do następnego y.

znalazłem rozwiązanie tego po prostu dwa razy przez tablicę:

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

Wiem, że był to konkretny przypadek pytania OP, ale piszę je w nadziei, że pomoże to komuś inaczej myśleć o ich problemie, jednocześnie zachowując prostotę.


Prawdopodobnie nie jest to Python. Jaki jest typ tablicy? Prawdopodobnie lista, ale co zawiera? Nawet jeśli zawiera ints, array.pop (x) prawdopodobnie nie zrobi tego, co chcesz.
Veky,

Trafne spostrzeżenie. Nie mogę znaleźć kodu, do którego się odwoływałem. Dla każdego, kto to czyta, array.pop (i) „Usuwa element z indeksem i z tablicy i zwraca go”. zgodnie z dokumentacją w języku Python. Trzeba więc uzyskać indeks elementu x w tablicy, aby ten kod działał zgodnie z oczekiwaniami. Istnieje również funkcja array.remove (x), która działałaby zgodnie z oczekiwaniami. Zmodyfikuję moją odpowiedź powyżej, aby naprawić ten błąd. Zakłada się, że druga tablica nie zawiera duplikatów, ponieważ array.remove (x) usunie tylko pierwszą instancję znalezionego x.
Nathan Garabedian

Ok, to rozumiem. W takim przypadku zwykłe użycie breakzamiast continuezrobiłoby to, co chcesz, prawda? :-)
Veky

Tak, dla wydajności i przejrzystości prawdopodobnie w tych przykładach chcesz użyć break zamiast kontynuować. :)
Nathan Garabedian

2

Spróbuj użyć nieskończonego generatora.

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

Korzystając z funkcji:

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

Spróbuj uruchomić powyższe kody, komentując returnrównież.

Bez użycia żadnej funkcji:

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

Teraz uruchom powyższe kody w pierwszej kolejności, a następnie spróbuj uruchomić, komentując każdą linię zawierającą breakjeden na raz od dołu.


2

Łatwym sposobem na przekształcenie wielu pętli w jedną, łamliwą pętlę jest użycie 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!

Musisz indeksować do swoich obiektów, w przeciwieństwie do możliwości jawnego iterowania wartości, ale przynajmniej w prostych przypadkach wydaje się to około 2-20 razy prostsze niż większość sugerowanych odpowiedzi.


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

prawdopodobnie mała sztuczka, jak poniżej, zrobi, jeśli nie woli refaktoryzacji do funkcji

dodano 1 zmienną break_level, aby kontrolować warunek pętli while

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

Możesz zdefiniować zmienną (na przykład break_statement ), a następnie zmienić ją na inną wartość, gdy wystąpi warunek dwóch przerw, i użyć jej w instrukcji if, aby również przerwać z drugiej pętli.

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

Dobra uwaga, jednak na każdym poziomie powyżej naszego wewnętrznego poziomu zainteresowania musielibyśmy przeskanować tę zmienną. Czuje się naprawdę źle, że język nie ma instrukcji GoTo pod względem wydajności.
Anatolij Alekseev,

1

Chciałbym przypomnieć, że funkcje w języku Python można tworzyć bezpośrednio w środku kodu i mogą one w przejrzysty sposób uzyskać dostęp do otaczających zmiennych w celu odczytu nonlocallubglobal deklaracji do pisania.

Możesz więc użyć funkcji jako „łamliwej struktury kontroli”, określającej miejsce, do którego chcesz wrócić:

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

Rozwiązania na 2 sposoby

Na przykład: czy te dwie macierze są równe / takie same?
macierz 1 i macierz 2 mają ten sam rozmiar, n, 2 macierze wymiarowe.

Pierwsze rozwiązanie , bez funkcji

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

Drugie rozwiązanie , z funkcją
Jest to ostateczne rozwiązanie dla mojej sprawy

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

Miłego dnia!


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

Mam nadzieję, że to pomaga:

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

Oto implementacja, która wydaje się działać:

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)

Jedyną wadą jest to, że musisz zdefiniować break_przed pętlami.


0

Nie można tego zrobić z poziomu języka. Niektóre języki mają błąd, inne mają przerwę, która wymaga argumentu, a python nie.

Najlepsze opcje to:

  1. Ustaw flagę sprawdzaną przez zewnętrzną pętlę lub ustaw stan zewnętrznej pętli.

  2. Umieść pętlę w funkcji i użyj return, aby zerwać wszystkie pętle jednocześnie.

  3. Przeformułuj swoją logikę.

Podziękowania należą się Vivek Nagarajan, programista od 1987 roku


Korzystanie z funkcji

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

Za pomocą flagi

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

Podobnie jak poprzednio, ale bardziej kompaktowy. (Booleany to tylko liczby)

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
Wygląda to dość brzydko i sprawia, że ​​kod jest trudniejszy do zrozumienia w porównaniu do poprzedniego. Poza tym to źle. Pomija sprawdzanie, czy dane wejściowe są akceptowalne i przerywa się po 1 pętli.
Eric

-3

Ponieważ pytanie to stało się standardowym pytaniem o włamanie do określonej pętli, chciałbym udzielić odpowiedzi na przykład, używając Exception.

Mimo że w konstrukcji wielokrotnie zapętlonej nie ma etykiety o nazwie zerwanie pętli, możemy skorzystać z wyjątków zdefiniowanych przez użytkownika, aby włamać się do wybranej przez nas pętli. Rozważ następujący przykład, w którym wydrukujmy wszystkie liczby do 4 cyfr w systemie numeracji base-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

Kiedy wypisujemy dane wyjściowe, nigdy nie otrzymamy żadnej wartości, której jednostką jest 4. W takim przypadku nie przerywamy żadnej pętli, ponieważ BreakLoop(4)jest ona podnoszona i przechwytywana w tej samej pętli. Podobnie, ilekroć dziesięć miejsc ma 3, włamy się do trzeciej pętli za pomocą BreakLoop(3). Ilekroć sto miejsc ma 5, BreakLoop(2)włamy się do drugiej pętli, a gdy tysiąc miejsc ma 2, włamujemy się do pierwszej pętli, używającBreakLoop(1) .

Krótko mówiąc, podnieś swój wyjątek (wbudowany lub zdefiniowany przez użytkownika) w wewnętrznych pętlach i złap go w pętli, z której chcesz wznowić kontrolę. Jeśli chcesz zerwać ze wszystkich pętli, złap wyjątek poza wszystkimi pętlami. (Nie pokazałem tego przypadku w przykładzie).

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.