Doorbreken van geneste lussen

Is er een gemakkelijkere manier om uit geneste lussen te breken dan een uitzondering te maken? (In Perl kun je elke lus een label geven en op zijn minst een buitenste lus voortzetten.)

for x in range(10):
    for y in range(10):
        print x*y
        if x*y > 50:
            "break both loops"

I.e. is er een leukere manier dan:

class BreakIt(Exception): pass
try:
    for x in range(10):
        for y in range(10):
            print x*y
            if x*y > 50:
                raise BreakIt
except BreakIt:
    pass

Antwoord 1, autoriteit 100%

Het is in ieder geval gesuggereerd, maar ook verworpen. Ik denk niet dat er een andere manier is, behalve het herhalen van de test of het reorganiseren van de code. Het is soms een beetje vervelend.

In het afwijzingsberichtzegt de heer van Rossum noemt het gebruik van return, wat erg verstandig is en iets dat ik persoonlijk moet onthouden. 🙂


Antwoord 2, autoriteit 96%

for x in xrange(10):
    for y in xrange(10):
        print x*y
        if x*y > 50:
            break
    else:
        continue  # only executed if the inner loop did NOT break
    break  # only executed if the inner loop DID break

Hetzelfde werkt voor diepere loops:

for x in xrange(10):
    for y in xrange(10):
        for z in xrange(10):
            print x,y,z
            if x*y*z == 30:
                break
        else:
            continue
        break
    else:
        continue
    break

Antwoord 3, autoriteit 44%

Als je de luscode in een functie kunt extraheren, kan een return-instructie worden gebruikt om de buitenste lus op elk moment te verlaten.

def foo():
    for x in range(10):
        for y in range(10):
            print(x*y)
            if x*y > 50:
                return
foo()

Als het moeilijk is om die functie te extraheren, kun je een innerlijke functie gebruiken, zoals @bjd2385 suggereert, bijvoorbeeld

def your_outer_func():
    ...
    def inner_func():
        for x in range(10):
            for y in range(10):
                print(x*y)
                if x*y > 50:
                    return
    inner_func()
    ...

Antwoord 4, autoriteit 29%

Gebruik itertools.product!

from itertools import product
for x, y in product(range(10), range(10)):
    #do whatever you want
    break

Hier is een link naar itertools.product in de Python-documentatie:
http://docs.python.org/library/itertools.html#itertools.product

Je kunt ook een array-begrip herhalen met 2 fors erin, en breken wanneer je maar wilt.

>>> [(x, y) for y in ['y1', 'y2'] for x in ['x1', 'x2']]
[
    ('x1', 'y1'), ('x2', 'y1'),
    ('x1', 'y2'), ('x2', 'y2')
]

Antwoord 5, autoriteit 24%

Soms gebruik ik een booleaanse variabele. Naïef, als je wilt, maar ik vind het heel flexibel en prettig om te lezen. Het testen van een variabele kan voorkomen dat complexe omstandigheden opnieuw worden getest en kan ook resultaten van verschillende tests in binnenlussen verzamelen.

   x_loop_must_break = False
    for x in range(10):
        for y in range(10):
            print x*y
            if x*y > 50:
                x_loop_must_break = True
                break
        if x_loop_must_break: break

Antwoord 6, autoriteit 12%

Als u een uitzondering wilt maken, kunt u een StopIteration-uitzondering opwerpen . Dat zal in ieder geval de bedoeling duidelijk maken.


Antwoord 7, autoriteit 4%

Je kunt je code ook refactoren om een generator te gebruiken. Maar dit is misschien niet een oplossing voor alle soorten geneste lussen.


Antwoord 8, autoriteit 2%

In dit specifieke geval kun je de lussen samenvoegen met een moderne python (3.0 en waarschijnlijk ook 2.6) door itertools.product te gebruiken.

Voor mezelf nam ik dit als vuistregel: als je te veel lussen nest (zoals in, meer dan 2), kun je meestal een van de lussen extraheren in een andere methode of de lussen samenvoegen tot één, zoals in dit geval.

Other episodes