Jak wyjść z klauzuli if

104

Jakie są rodzaje metod przedwczesnego wychodzenia z ifklauzuli?

Są chwile, kiedy piszę kod i chcę umieścić breakinstrukcję wewnątrz ifklauzuli, tylko po to, aby pamiętać, że mogą one być używane tylko do pętli.

Jako przykład weźmy następujący kod:

if some_condition:
   ...
   if condition_a:
       # do something
       # and then exit the outer if block
   ...
   if condition_b:
       # do something
       # and then exit the outer if block
   # more code here

Mogę wymyślić jeden sposób, aby to zrobić: zakładając, że przypadki wyjścia mają miejsce w zagnieżdżonych instrukcjach if, zawiń pozostały kod w duży blok else. Przykład:

if some_condition:
   ...
   if condition_a:
       # do something
       # and then exit the outer if block
   else:
       ...
       if condition_b:
           # do something
           # and then exit the outer if block
       else:
           # more code here

Problem polega na tym, że więcej lokalizacji wyjścia oznacza więcej zagnieżdżonego / wciętego kodu.

Alternatywnie mógłbym napisać kod, aby ifklauzule były jak najmniejsze i nie wymagały żadnych wyjść.

Czy ktoś zna dobry / lepszy sposób na wyjście z ifklauzuli?

Jeśli są jakieś powiązane klauzule else-if i else, myślę, że zakończenie spowoduje ich pominięcie.

rzymski
źródło
2
Drugi przykład kodu - czy wiesz o tym elif?
Craig McQueen
2
„Alternatywnie mógłbym napisać kod tak, aby klauzule if były jak najmniejsze i nie wymagały żadnych wyjść”. - i na pewno byłby to najlepszy sposób działania. :-)
Michał Marczyk
2
@Craig McQueen: Tak, ale mówię, że chciałem, aby kod był wykonywany między instrukcjami warunków? Np if a: #stuff; #stuff_inbetween; if b: #stuff;. Kod pośredni zależy od, not aale nie zależy od b.
Roman
cześć, proszę, nie zapomnij elif stackoverflow.com/a/2069680/7045119
kerbrose

Odpowiedzi:

99

(Ta metoda działa dla ifs, wielu zagnieżdżonych pętli i innych konstrukcji, z których nie można breakłatwo.)

Zawiń kod w jego własnej funkcji. Zamiast breakużywać return.

Przykład:

def some_function():
    if condition_a:
        # do something and return early
        ...
        return
    ...
    if condition_b:
        # do something else and return early
        ...
        return
    ...
    return

if outer_condition:
    ...
    some_function()
    ...
Drew Dormann
źródło
4
Z przyjemnością dodam do twojej torby sztuczki programisty. Z mojego doświadczenia wynika, że ​​takie podejście działa prawie za każdym razem, gdy masz ochotę użyć ruchu do przodu. (I jednocześnie wskazuje i rozwiązuje sytuacje, w których pojedyncza funkcja staje się zbyt duża)
Drew Dormann
2
W idealnym przypadku można osiągnąć oba te cele, ale są chwile, kiedy trzeba zamienić dobry kod na dobrą wydajność. Te czasy są rzadkie, zwłaszcza gdy rozważasz użycie Pythona. Innymi słowy: nie przejmuj się zbytnio narzutem wywołań funkcji.
ephemient
17
Jest taka stara anegdota: „Dennis Ritchie zachęcał do modułowości, mówiąc wszystkim i różnym, że wywołania funkcji są naprawdę, bardzo tanie w C. Wszyscy zaczęli pisać małe funkcje i modularyzować. Wiele lat później odkryliśmy, że wywołania funkcji są nadal drogie na PDP-11 , a kod VAX często spędzał 50% czasu na instrukcji CALLS. Dennis nas okłamał! Ale było już za późno; wszyscy byliśmy uzależnieni ... ”
ephemient
1
@ephemient: To zabawne, czy jest coś więcej w tej historii? Chciałbym przeczytać całość.
Roman
4
Ten cytat pochodzi z rozdziału 4 książki The Art of Unix Programming (online pod adresem faqs.org/docs/artu ). Naprawdę powinieneś przeczytać całość, jeśli nie zrobiłeś tego wcześniej.
ephemient
55
from goto import goto, label

if some_condition:
   ...
   if condition_a:
       # Zrób coś
       # a następnie wyjdź z zewnętrznego bloku if
       goto .end
   ...
   if condition_b:
       # Zrób coś
       # a następnie wyjdź z zewnętrznego bloku if
       goto .end
   # więcej kodu tutaj

etykieta .end

(Nie używaj tego, proszę.)

ephemient
źródło
35
+1, bo to jest zabawne. Wyszukiwanie w Google ujawniło mi, że był to moduł żartów primaaprilisowych.
Roman
2
Ja też się z tym połączyłem. Kliknij pierwszy goto.
ephemient
1
to przypomina mi kod asemblera z
różnymi
2
@ephemient: Ach, nie zauważyłem linku. Pomyślałem, że to wyróżnianie kodu. Ale teraz, kiedy patrzę na twój kod, nie widzę żadnego prawdziwego wyróżnienia ...
Roman
1
Kiedy PHP wprowadziło goto, przeszedłem do Pythona php.net/manual/en/control-structures.goto.php
Marc
25
while some_condition:
   ...
   if condition_a:
       # do something
       break
   ...
   if condition_b:
       # do something
       break
   # more code here
   break
Thomas Eding
źródło
3
Och, hej, naprawdę podoba mi się ten pomysł. Myślę, że to dokładnie rozwiązuje moje pierwotne pragnienie. Mam jednak dręczące wrażenie, że to nie jest dobra praktyka (iz tego powodu na razie zachowam obecną akceptowaną odpowiedź, ponieważ promuje ona dobry styl kodowania).
Roman
6
Zwróć uwagę, że możesz zachować oryginał i owinąć całość w plik while True:. Tylko pamiętaj, aby breakna końcu zamieścić oświadczenie! W przypadku języków z konstrukcją do-while jest to bardziej idomatyczne:do { code that can conditionally break out } while (false);
Thomas Eding
10

Możesz emulować funkcjonalność goto z wyjątkami:

try:
    # blah, blah ...
    # raise MyFunkyException as soon as you want out
except MyFunkyException:
    pass

Zastrzeżenie: chcę tylko zwrócić uwagę na możliwość robienia rzeczy w ten sposób, podczas gdy w żaden sposób nie popieram tego jako rozsądnego w normalnych okolicznościach. Jak wspomniałem w komentarzu do pytania, zdecydowanie lepszym rozwiązaniem jest ustrukturyzowanie kodu tak, aby w pierwszej kolejności uniknąć bizantyjskich warunków warunkowych. :-)

Michał Marczyk
źródło
Haha, podoba mi się to kreatywne rozwiązanie. Chociaż dotrzymam twojego zastrzeżenia i nie użyję takiego dziwacznego kodu.
Roman
@Roman: Cieszę się, że mogę dodać kolejną sztuczkę obok Shmoopty'ego - nawet jeśli w porównaniu czuję się niegrzeczny. ;-)
Michał Marczyk
8

może to?

if some_condition and condition_a:
       # do something
elif some_condition and condition_b:
           # do something
           # and then exit the outer if block
elif some_condition and not condition_b:
           # more code here
else:
     #blah
if
ghostdog74
źródło
1
Tak, to może zadziałać. Wydaje mi się, że kiedy to pisałem, mój umysł trochę się wyłączył elif. Chociaż myślę, że to nie zadziała w sytuacji, w której chcę, aby kod był wykonywany między zagnieżdżonymi instrukcjami if.
Roman
to jest właściwie prawidłowa odpowiedź. dlaczego nie widzę, żeby ludzie to polecali? :)
kerbrose
6

O co właściwie pytano, moje podejście polega na umieszczeniu ich ifw pętli z jedną pętlą

while (True):
    if (some_condition):
        ...
        if (condition_a):
            # do something
            # and then exit the outer if block
            break
        ...
        if (condition_b):
            # do something
            # and then exit the outer if block
            break
        # more code here
    # make sure it is looped once
    break

Sprawdź to:

conditions = [True,False]
some_condition = True

for condition_a in conditions:
    for condition_b in conditions:
        print("\n")
        print("with condition_a", condition_a)
        print("with condition_b", condition_b)
        while (True):
            if (some_condition):
                print("checkpoint 1")
                if (condition_a):
                    # do something
                    # and then exit the outer if block
                    print("checkpoint 2")
                    break
                print ("checkpoint 3")
                if (condition_b):
                    # do something
                    # and then exit the outer if block
                    print("checkpoint 4")
                    break
                print ("checkpoint 5")
                # more code here
            # make sure it is looped once
            break
izzulmakin
źródło
1
Szczerze mówiąc, to najczystsze rozwiązanie. Jest bardzo jasne, kiedy kod ma zostać zakończony - nie musisz się martwić o zakres funkcji wewnątrz funkcji, a wydajność lub dług logiczny są zerowe.
Trent
2
Można rozważyć użycie for _ in range(1):zamiast while True:. (1) Lepiej przedstaw swój zamiar pojedynczej pętli iteracyjnej i (2) brak instrukcji ostatniej przerwy, aby wyjść z pętli (może zostać później usunięta przez przypadek)
Bernhard Kausler
3

Ogólnie mówiąc, nie. Jeśli zagnieżdżasz "jeśli" i odrywasz się od nich, robisz to źle.

Jeśli jednak musisz:

if condition_a:
   def condition_a_fun():
       do_stuff()
       if we_wanna_escape:
           return
   condition_a_fun()
if condition_b:
   def condition_b_fun():
       do_more_stuff()
       if we_wanna_get_out_again:
           return
   condition_b_fun()

Zwróć uwagę, że funkcje nie MUSZĄ być deklarowane w instrukcji if, można je zadeklarować z wyprzedzeniem;) Byłby to lepszy wybór, ponieważ pozwoli to uniknąć późniejszej refaktoryzacji brzydkiego „jeśli / potem”.

Enki
źródło
Dziękuję za odpowiedź. Nie jestem pewien, co masz na myśli mówiąc „pętle zagnieżdżające”. Jeśli odnosisz się do mojej wzmianki o słowie kluczowym „przerwa”, po prostu próbowałem zmotywować moje poszukiwania wyjścia if-exit, porównując je z istnieniem wyjścia pętli. Ponadto nie jestem pewien, jak Twój kod rozwiązuje problem, tak jak w moim przykładzie if condition_ai if condition_bzagnieżdżony w pliku if some_condition. Chcę móc wyrwać się z if some_condition.
Roman
Powodem, dla którego ludzie chcą zagnieżdżać ifs i łamać je, prawdopodobnie nie jest to, że robią to źle, ale może dlatego, że chcą napisać czysty, prosty i SUCHY kod. Prosty przypadek to sytuacja, gdy program musi wykonać x (), gdy spełnione są oba warunki warunek_a i warunek_b, i musi wykonać y () tylko dla warunku_a, a z () tylko dla warunku_b. a koder odmawia wielokrotnego zapisu x ()
izzulmakin
1

W rzeczywistości to, co opisujesz, to instrukcje goto, które są generalnie dość mocno panoramowane. Twój drugi przykład jest dużo łatwiejszy do zrozumienia.

Jednak czystszy nadal byłby:

if some_condition:
   ...
   if condition_a:
       your_function1()
   else:
       your_function2()

...

def your_function2():
   if condition_b:
       # do something
       # and then exit the outer if block
   else:
       # more code here
Kowalstwo
źródło
1

Jest inny sposób, który nie polega na definiowaniu funkcji (ponieważ czasami jest to mniej czytelne dla małych fragmentów kodu), nie używa dodatkowej zewnętrznej pętli while (która może wymagać szczególnego uznania w komentarzach, aby być zrozumiałym nawet na pierwszy rzut oka) , nie używa goto (...) i co najważniejsze, pozwólmy ci zachować poziom wcięcia dla zewnętrznego, jeśli tak, nie musisz zaczynać zagnieżdżania rzeczy.

if some_condition:
   ...
   if condition_a:
       # do something
       exit_if=True # and then exit the outer if block
if some condition and not exit_if: # if and only if exit_if wasn't set we want to execute the following code
   # keep doing something
   if condition_b:
       # do something
       exit_if=True # and then exit the outer if block
if some condition and not exit_if:
   # keep doing something

Tak, to również wymaga ponownego spojrzenia na czytelność, jednak jeśli fragmenty kodu są małe, nie wymaga to śledzenia żadnych pętli while, które nigdy się nie powtórzą, a po zrozumieniu, do czego służą pośrednie, jeśli są, są łatwe do odczytania, wszystko w w jednym miejscu iz tym samym wcięciem.

I powinno być całkiem wydajne.

DonQuiKong
źródło
0

Więc rozumiem, że próbujesz wyrwać się z zewnętrznego bloku if code

if some_condition:
    ...
    if condition_a:
       # do something
       # and then exit the outer if block
       ...
    if condition_b:
       # do something
       # and then exit the outer if block
# more code here

Jednym ze sposobów rozwiązania tego problemu jest to, że możesz przetestować pod kątem fałszywego warunku w zewnętrznym bloku if, który następnie niejawnie wyjdzie z bloku kodu, a następnie użyj innego bloku else do zagnieżdżenia drugiego ifs, aby coś zrobić

if test_for_false:
    # Exit the code(which is the outer if code)

else:
    if condition_a:
        # Do something

    if condition_b:
        # Do something
Romeo
źródło
0

Jedyną rzeczą, która zastosowałaby to bez dodatkowych metod, jest elifponiższy przykład

a = ['yearly', 'monthly', 'quartly', 'semiannual', 'monthly', 'quartly', 'semiannual', 'yearly']
# start the condition
if 'monthly' in b: 
    print('monthly') 
elif 'quartly' in b: 
    print('quartly') 
elif 'semiannual' in b: 
    print('semiannual') 
elif 'yearly' in b: 
    print('yearly') 
else: 
    print('final') 
kerbrose
źródło
-1

Oto inny sposób radzenia sobie z tym. Używa pojedynczego elementu dla pętli, która umożliwia po prostu użycie kontynuacji. Zapobiega niepotrzebnej potrzebie posiadania dodatkowych funkcji bez powodu. A dodatkowo eliminuje potencjalne nieskończone pętle while.

if something:
    for _ in [0]:
        # Get x
        if not x:
            continue

        # Get y
        if not y:
            continue

        # Get z
        if not z:
            continue

        # Stuff that depends on x, y, and z
Timbot
źródło
-2

użycie returnw warunku if zwróci cię z funkcji, więc możesz użyć return do złamania warunku if.

Nikhil Parashar
źródło
2
chce przełamać, jeśli nie wyjść z funkcji
StealthOne