user2966098
user2966098

Reputation: 171

Python - Any way to avoid several if-statements inside each other in a for-loop?

I need a better way to do this. I'm new with programming but I know that this is a very inefficient way of doing it and that I need a function for this, I just don't know how to do it exactly. any suggestions? I'm VERY grateful for any help!

for H in range(0,len(a_list)):
    if a_list[H] > list4[0]:
        list5 = [number_list[i]]
        if function(list1,list5) == list1[1]:
            if function(list2,list5)== list2[1]:
                if function(list3,list5)== list3[1]:
                    if function(list4,list5)== list4[1]:
                        list5.append(input('some input from the user'))
                        other_function(list5)
                        if list5[1]== 40:
                            print ('something something')
                            break out of EVERY loop 
                         else: 
                            for H in range(0,len(a_list)):
                                if a_list[H] > list5[0]:
                                    list6 = [number_list[i]]
                                    if function(list1,list6) == list1[1]:
                                        if function(list2,list6)== list2[1]:
                                            if function(list3,list6)== list3[1]:
                                               if function(list4,list6)== list4[1]:
                                                  if function(list5,list6)== list5[1]:
                                                     list6.append(input('some input from theuser'))
                                                     other_function(list6)
                                                         if list6[1]== 40:
                                                             print ('something something')
                                                                 break out of EVERY loop 
                                                         else: 
                                                            etc. (one extra comparison every time)  

Upvotes: 15

Views: 11514

Answers (2)

leewz
leewz

Reputation: 3346

When you have three or more numbered and similarly-used variables, think lists.

With that in mind, we first change list1, list2, list3, ... into a list of lists (indexed 0,1,2,3 instead of 1,2,3,4). Except don't call it list, because that's a useful name for something that's already useful. lst is pretty popular in Python. I'm also going to change list5 into lstA and list6 into lstB because 5 and 6 no longer make sense.

Now we have this:

for H in range(0,len(a_list)):
    if a_list[H] > lst[3][0]:
        lstA = [number_list[i]]
        if function(lst[0],lstA) == lst[0][1]:
            if function(lst[1],lstA)== lst[1][1]:
                if function(lst[2],lstA)== lst[2][1]:
                    if function(lst[3],lstA)== lst[3][1]:
                        lstA.append(input('some input from the user'))
                        other_function(lstA)
                        if lstA[1]== 40:
                            print ('something something')
                            break out of EVERY loop 
                         else: 
                            for H in range(0,len(a_list)):
                                if a_list[H] > lstA[0]:
                                    lstB = [number_list[i]]
                                    if function(lst[0],lstB) == lst[0][1]:
                                        if function(lst[1],lstB)== lst[1][1]:
                                            if function(lst[2],lstB)== lst[2][1]:
                                               if function(lst[3],lstB)== lst[3][1]:
                                                  if function(lstA,lstB)== lstA[1]:
                                                     lstB.append(input('some input from theuser'))
                                                     other_function(lstB)
                                                         if lstB[1]== 40:
                                                             print ('something something')
                                                                 break out of EVERY loop 
                                                         else: 
                                                            etc. (one extra comparison every time)  

Now it's more obvious that we're basically doing the same thing four times.


When you have to do the same thing a bunch of times, think loops.

We'll change the blocks into loops. We'll also use a flag variable to keep track of whether something failed while testing our logic, and use the logic "if it does not work, skip stuff" rather than "if it works, do stuff"

for H in range(0,len(a_list)):
    if a_list[H] > lst[3][0]:
        continue #reducing indent levels by negating the check:
                 #quit on failure instead of work on success

    lstA = [number_list[i]]

    quit = False

    for j in range(4):
        if function(lst[j],lstA) != lst[j][1]: #testing FALSEHOOD
            quit = True
            break #the j loop only

    if quit:
        continue #reducing indent levels by negating the check

    lstA.append(input('some input from the user'))
    other_function(lstA)
    if lstA[1]== 40:
        print ('something something')
        break #out of EVERY loop
    #else: #don't need the else because we broke

    for H in range(0,len(a_list)):
        if not a_list[H] > lstA[0]:
            continue #reducing indent levels by negating the check

        lstB = [number_list[i]]

        for j in range(4):
            if function(lst[j],lstB) != lst[j][1]: #testing FALSEHOOD
                quit = True;
                break #to the H loop
        if not quit and  function(lstA,lstB)== lstA[1]: #combining two checks
            lstB.append(input('some input from theuser'))
            other_function(lstB)
            if lstB[1]== 40:
                print ('something something')
                break #out of EVERY loop
            else: #at this point I'm lost and can't refactor
                etc. (one extra comparison every time)  

When you have to break out of multiple loops at once, think functions and returning instead of breaking. Or exceptions and try blocks, but some might find that distasteful.

The failure flag works, but isn't very elegant. There's an exaggerated saying: "... if you need more than 3 levels of indentation, you're screwed anyway, and should fix your program." Read this as: If you have a lot of levels of indentation (and some languages require more than others), you should think about whether you can move some of the logic into a function.

We'll also move some repeated logic into a checker function.

(Finally, I think it's a bug that your second for-loop is nested in your first. Since they have the same iterator variable H, I think that would cause an infinite loop. So I fixed that.)

#returns FALSE if a check fails, unlike the `quit` variable
def checker(lst, lstA):
    for i in range(4):
        if function(lst[i],lstA) != lst[i][1]: #testing FALSEHOOD
            return False;
    return True;


def main(???):
    for H in range(0,len(a_list)):
        if a_list[H] > lst[3][0]:
            continue

        lstA = [number_list[i]]

        if not checker(lst,lstA):
            continue

        lstA.append(input('some input from the user'))
        other_function(lstA)
        if lstA[1]== 40:
            print ('something something')
            return #break out of EVERY loop

    for H in range(0,len(a_list)):
        if not a_list[H] > lstA[0]:
            continue

        lstB = [number_list[i]]

        if checker(lst,lstB) and  function(lstA,lstB) == lstA[1]:
            lstB.append(input('some input from theuser'))
            other_function(lstB)
            if lstB[1]== 40:
                print ('something something')
                return # break out of EVERY loop
            else: #at this point I'm lost and can't refactor
                etc. (one extra comparison every time)  

Upvotes: 5

Martijn Pieters
Martijn Pieters

Reputation: 1124928

Use the all() function to test multiple related conditions:

if all(function(lst, list5) == lst[1] for lst in (list1, list2, list3, list4)):

and

if all(function(lst, list6) == lst[1] for lst in (list1, list2, list3, list4, list5)):

Like the nested if statements, all() will short-circuit; return False as soon as any of the tests fails.

In addition, loop over lists directly, instead of generating a range of indices. If you are using break, you don't need to use else either, removing another level of indentation.

You can remove another level by filtering a_list:

for H in filter(lambda H: H > list4[0], a_list):

Together, this reduces your nesting to:

for H in filter(lambda H: H > list4[0], a_list):
    list5 = [number_list[i]]
    if all(function(lst, list5) == lst[1] for lst in (list1, list2, list3, list4)):
        list5.append(input('some input from the user'))
        other_function(list5)
        if list5[1]== 40:
            print ('something something')
            break # out of EVERY loop 

        for J in filter(lambda J: J >list5[0], a_list):
            if all(function(lst, list6) == lst[1] for lst in (list1, list2, list3, list4, list5)):
            list6.append(input('some input from theuser'))
            other_function(list6)
            if list6[1]== 40:
                print ('something something')
                break # out of EVERY loop 

            # continue here

Presumably your break statements actual use exceptions (raise CustomException() and try:, except CustomException: # break out of all the loops fast), as a regular break would only stop the current loop.

If you are continuously adding further lists and nesting, you probably want use a list to hold all those nested lists, then simply add to the outer list:

class EndLoops(Exception): pass

stack = [[number_list[0]]]
try:
    for i in number_list[1:]:
        for H in filter(lambda H: H > stack[-1][0], a_list):
            stack.append([i])
            if all(function(lst, stack[-1]) == lst[1] for lst in stack[:-1]):
                stack[-1].append(input('some input from the user'))
                other_function(stack[-1])
                if stack[-1][1] == 40:
                    print ('something something')
                    raise EndLoops
except EndLoops:
    pass # broken out of outer loop

and suddenly all the nesting has gone; instead you moved the nesting to a stack list of lists.

Note that I don't know what the outer-most loop looks like in your code, I just took an educated stab in the dark at it, but the idea should be roughly correct.

Upvotes: 13

Related Questions