Search code examples
pythonlistwhile-loopbreakterminate

How to return the simplified direction list from a given direction list by eliminating pairs of consecutive complimentary directions?


I want to return the simplest direction set from a list of given directions. So, if the direction set has "SOUTH" followed by "NORTH", or vice versa, they should cancel out each other; same with "WEST" followed by "EAST", or vice versa.

So, for instance, if the given direction list is ["NORTH", "SOUTH", "SOUTH", "EAST", "WEST", "NORTH", "WEST"], the correct output should be to return the simplified list ['WEST']. My logic is to do it in the following way:

  • The list has 7 elements. In the first pass, the elements 0 & 1 get removed ("NORTH", "SOUTH"), and 3 & 4 also get removed ("EAST", "WEST"). So the list is now ['SOUTH', 'NORTH', 'WEST']. Compare the length of the list before and after the removal; if they are the same, break - else, repeat. Since the old and new lengths are 7 and 3 respectively, it repeats the process.
  • The list now has 3 elements. In the second pass, elements 0 & 1 get cancelled ('SOUTH', 'NORTH'). So the list now becomes ['WEST']. Again, Compare the length of the list before and after the removal; if they are the same, break - else, repeat. Since the old and new lengths are 3 and 1 respectively, it repeats the process.
  • In the third pass, no element pair gets removed. Compare the length of the list before and after the removal; if they are the same, break - else, repeat. Since the old and new lengths are 1 and 1 respectively, it breaks the process and returns the list as ['WEST'].

My code to implement this is as follows:

arr = ["NORTH", "SOUTH", "SOUTH", "EAST", "WEST", "NORTH", "WEST"]

while True:
    for i in range(len(arr)):
        try:
            len_old = len(arr)
            if ( ((arr[i]=='NORTH' and arr[i+1]=='SOUTH') or (arr[i]=='SOUTH' and arr[i+1]=='NORTH')) or
                 ((arr[i]=='EAST' and arr[i+1]=='WEST') or (arr[i]=='WEST' and arr[i+1]=='EAST')) ):
                arr.remove(arr[i])
                arr.remove(arr[i])
            len_new = len(arr)

            if len_new==len_old:
                break

        except:
            pass

arr

But the problem is, it never terminates. When I manually force the code to stop and check the value of of the list and the old and new lengths of the list, it returns the correct values:

print(arr)
print(len_new)
print(len_old)
>>>
['WEST']
1
1

So, what's wrong with the code? Why does it not break despite reaching the break condition, and how do I fix it?


Solution

  • Your program will never terminate. Outer look does not have any termination condition. Interloop termination may not execute for all input. I have rewritten the code, it is working for provided test cases

    def dirReduc(arr):
    
            if len(arr)<=1:
    
                return arr
    
            len_old = len(arr)
            arr = checkDirection(arr)
            len_new = len(arr)
    
            if len_new==len_old:
                return arr
            else:
                arr= dirReduc(arr)
    
            return arr
    
    
    def checkDirection(arr):
            if len(arr)<=1:
                return arr
            for i in range(len(arr)-1):
                try:
                    if ( ((arr[i]=='NORTH' and arr[i+1]=='SOUTH') or (arr[i]=='SOUTH' and arr[i+1]=='NORTH')) or
                         ((arr[i]=='EAST' and arr[i+1]=='WEST') or (arr[i]=='WEST' and arr[i+1]=='EAST')) ):
                        arr.remove(arr[i])
                        arr.remove(arr[i])
                        return arr
                except:
                    print('Catching Except')
                    return arr
    
    
    
        main_arr = ["NORTH", "SOUTH", "SOUTH", "EAST", "WEST", "NORTH", "WEST"]
        main_arr = dirReduc(main_arr)
        print('Final Result')
        print(main_arr)