Strange recursion behaviour in python - python

My sudoku solver does exactly what it's supposed to do - except returning the correct thing. It prints what it should right before the return (a correctly solved grid), but then it seems to keep running for a bit and returns None. I can't figure out what's going on.
Grid is a list of lists. Assume that check_sudoku returns True if the grid is valid (solved or not), and False otherwise.
def solve_sudoku(grid, row=0, col=0):
"Searches for valid numbers to put in blank cells until puzzle is solved."
# Sanity check
if not check_sudoku(grid):
return None
# Sudoku is solved
if row > 8:
return grid
# not a blank, try next cell
elif grid[row][col] != 0:
next_cell(grid, row, col)
else:
# try every number from 1-9 for current cell until one is valid
for n in range(1, 10):
grid[row][col] = n
if check_sudoku(grid):
next_cell(grid, row, col)
else:
# Sudoku is unsolvable at this point, clear cell and backtrack
grid[row][col] = 0
return
def next_cell(grid, row, col):
"Increments column if column is < 8 otherwise increments row"
return solve_sudoku(grid, row, col+1) if col < 8 else solve_sudoku(grid, row+1, 0)

You're calling next_cell in the recursion, but never returning its value.

It seems to me like this will never actually return something useful. The first time we enter your solve_sudoku, you check to see if the grid is solved, and if so you return it. After that, you begin a bunch of recursion which ultimately will end up coming back out towards the end of the function and returning None. No matter what, you are returning None all the time. The only thing that you end up with is a modified grid argument that you passed in to start.
def solve_sudoku(grid, row=0, col=0):
# possible return at the first entry point
if not check_sudoku(grid):
return None
# only time you would ever NOT get None
if row > 8:
return grid
...recurse...
# come back out when the stack unwinds,
# and there is no return value other than None
What I speculate is happening, is that you are printing the values along the way, and you do properly see a solved grid at the moment it happens, but your function isn't set up to properly completely break out when that is done. It continues to loop around until it exhausts some range and you see a bunch of extra work.
The important thing to do is to check the return value of the recursive call. You would either return None if its not solved, or grid if it is. As it stands, you never care about the result of your recursions in the calling scope.
Because I don't have all the details about your specific code, here is a simple equivalent:
def solver(aList):
if aList[0] == 10:
print "SOLVED!"
return None
print "NOT SOLVED..."
return next(aList)
def next(aList):
# do some stuff
# check some stuff
aList[0] += 1
return solver(aList)
if __name__ == "__main__":
data = [0]
solver(data)
print data
Notice that the indirectly recursive call to checker() -> solver() has its value returned all the way up the chain. In this case we are saying None means solved, and otherwise it should keep recursing. You know that at some point deep in your recursion stack, the solution will be solved. You will need to communicate that back up to the top right away.
The communication looks like this:
aList == [0]
solver(aList)
next(...)
next(...)
next(...)
next(...)
#SOLVED
<- next(...)
<- next(...)
<- next(...)
<- next(...)
<-solver(aList)
aList == [10]
And here is what your version might look like if applied to my simple example:
aList == [0]
solver(aList)
next(...)
next(...)
# SOLVED
next(...)
next(...)
...
<- next(...)
<- next(...)
<- next(...)
<- next(...)
<-solver(aList)
aList == [10]

Related

Python: How to exit all the recursions in a recursive function [duplicate]

I'm wondering how to break out of a recursive loop to the main function. I am trying to do a simple palindrome exercise. The function should return True for "redivider" but the return True is being passed to is_pal() and the function isn't breaking. Short of adding a second variable to is_pal to track True/False, what is the proper way to break out of this recursive loop?
def first(word):
return word[0]
def last(word):
return word[-1]
def middle(word):
return word[1:-1]
def is_pal(str):
if len(str) == 1:
return True
if first(str) == last(str) and len(str) > 1:
is_pal(middle(str))
print is_pal("redivider")
One way to break out of a recursive function in Python is to throw an exception and catch that at the top level. Some people will say that this is not the right way to think about recursion, but it gets the job done. Furthermore, if the task is to identify "problem" elements in an array/array of arrays/ndarray etc., a break technique is convenient, because it stops the algorithm from continuing after the global solution has been identified.
def solve_problem(lst):
def solve_rec(l):
'''has impl. that may throw an exception '''
try:
solve_rec(lst)
return True
except:
return False
def is_pal(str):
if len(str) <= 1:
return True
if first(str) == last(str):
return is_pal(middle(str))
else:
return False
That way, if they don't match, False is returned; if it makes it all the way to the end, True is returned. I also eliminated a redundant conditional and checked for the edge-case of an even-length palindrome.
You don't "break" out of recursive functions. Trying to do so says you're thinking about them the wrong way. Currently your recursive call is ignoring the output, which means that the recursion is pointless; whatever is_pal(middle(str)) returns has no effect on the return value of your function.
A recursive algorithm solves the input problem by decomposing the problem into a smaller problem, getting the solution to the smaller problem recursively, and then using the smaller solution to construct a correct solution to the larger problem. You don't "break" out of the inner calls, you return a solution back up one level. You don't know (or need to know) whether you're in an inner call or a top level call. In either case, your function should do the same thing: return True if the argument is a palindrome, and False if it isn't.
The algorithm you're trying to implement is basically this:
If the string is of length 1, it's a palindrome (return True)
Otherwise, if the first character is the same as the last character, then the input is a palindrome if the middle characters are a palindrome.
So what this means is that once you've established the first and last characters are the same, the answer to "is my input a palindrome" is exactly the same as the answer to "are the middle characters a palindrome". You need to return that answer to fulfil your contract. So the recursive call should be return is_pal(middle(str)) rather than just is_pal(middle(str)). If this was a top level call, then that's the answer; if this wasn't a top-level call, then the outer call is going to need this answer to work out the answer to the outer problem (in this case, by simply returning it).
Btw, your algorithm also has some other problems.
You never return False, so the answer can never be False (in this case you happen to accidentally return None by falling off the end of the function if the first and last character don't match, and None will probably do as a stand in for False in most cases, but it's still not really correct).
If the string's length is zero rather than 1 (which will happen if an empty string is passed in or if a palindrome of even length is passed in once all the pairs of equal first and last characters are stripped off), then you don't return the correct answer, and in fact you try to get the first and last character of the empty string, which will cause an exception.
You can exit the program after printing the results using the exit() function.
That may not be a good practice, but it might be what you're looking for.
You're missing a return. Also, don't use str as a variable name. Last thing, the first and last functions could be named slightly better.
def first_letter(word):
return word[0]
def last_letter(word):
return word[-1]
def middle(word):
return word[1:-1]
def is_pal(word):
if len(word) == 1:
return True
if first_letter(word) == last_letter(word) and len(word) > 1:
return is_pal(middle(word))
print is_pal("redivider")
You need to return False in case they don't match and add a return statement. Also you will probably want to check against len(str)==0 and len(str)==1:
def is_pal(str):
if len(str) in [0, 1]:
return True
if first(str) == last(str) and len(str) > 1:
return is_pal(middle(str))
else :
return False
YOU CAN BREAK RECURSION BY RETURNING 1 in 'if' statement before you write your 'function()' 2nd time.
I mean that's what we do to find factorial !! RIGHT? :)

Can return only be used once in a block of code?

I'm in the process of learning python, and I can't wrap my head around a piece of code in the book:
def find_value(List, value):
for i in range(len(List)):
if List[i] == value:
return i
return -1
I've tried running the code, and it returns the index if the value is in it, and returns -1 if it isn't, but what I don't understand is since the 'return -1' is outside the for loop, and if statement, should it not be run every time the code is executed?
Or does return only get processed once, the first time it is called?
I just want to make sure I understand this concept before moving on.
Thanks in advance
No, you can have as many return statements in as many places as you like:
def function():
... # do something
return 1
return
return [3, 4, 5]
return next(__import__('os').walk('.'))[-1:-1000000000:-1]
Though the important thing to keep in mind that only the first return that your function encounters will count, and anything else that would've succeeded it is not touched.
In the function above, you'll get 1 as the result and nothing else.
This sort of "flow of control" is why you can even do weird stuff like this -
def foo():
return True
whatIsThisIdontEven__somethingWeird.jpg # would ordinarily throw RuntimeErrors anywhere else
print(foo())
# True
In your case, it entirely depends on your code flow at runtime, but you'll still end up encountering only one return, and consequently returning only once.
Note that one difference is in the case of try-except-finally, where the return in the final clause always wins.
def function():
try:
... # do something
return 1
except SomeException:
... # do something else
finally:
return 2
In the case of normal execution, you'll encounter return 1 in try, but because of the semantics of the finally clause, you'd still end up returning 2. I believe this is the only exception.
Now, yield, on the other hand, is a different matter...
Once the function sees a return statement, the function will end and return the variable in the return statement. So the rest of the function will not be executed once the function comes across a return statement.
The return statement causes your function to exit and hand back a
value to its caller. The point of functions in general is to take in
inputs and return something.
Keep in mind : function return one at a time by memory .
So when you start the loop and then 'if' condition goes true so function return and exit.
if List[i] == value:
return i
and if you have to return many items then don't return the output instead of store the output in a list and return that list at last .
def find_value(List, value):
return_list=[]
for i in range(len(List)):
if List[i] == value:
return_list.append(i)
return return_list
In you code you wanted two return so you can try conditional return like this:
def find_value(List, value):
return_list=[]
for i in range(len(List)):
if List[i] == value:
return_list.append(i)
if return_list:
return return_list
else:
return -1
print(find_value([1,2,3,4,5,6],4))

Python - Matrix IndexError exception not working

I am trying to write a function which enables a move 1 unit to the left (in the x-axis) in a grid. It should only work if there is a valid coordinate to the left, and if not (i.e. the start point is on the edge of the grid), the function should return None. Here is my attempt:
def left_move(point):
try:
LEFT = grid[point[1]][point[0]-1]
except IndexError:
return None
if LEFT == '.':
change = [-1,0]
for idx, amount in enumerate(change):
point[idx]+=amount
return point
return None
Say the starting point is [0,3] and therefore on the edge of the grid and I only run the top part of the code:
def left_move(point):
try:
LEFT = grid[point[1]][point[0]-1]
except IndexError:
return None
It returns None as expected. But if the whole block is executed it returns [-1,3] which is outside of the grid and shouldn't be allowed by the try-except . Why is this? And how can it be corrected?
This is because Python's interpretation of -1 in array indexing is the last element of the array. -1 is a legal way of referencing array elements and thus will not trigger an IndexError exception.
>>> a = [0,1,2]
>>> print a[-1]
2
You would need to manually check if the value you are given resolves to -1 and then handle that accordingly.
Here, you need to place the rest of the code inside the try statement. Otherwise, it will try, fail, and then run the rest of the code.
def left_move(point):
if point[0]-1 != -1:
LEFT = grid[point[1]][point[0]-1]
if LEFT == '.':
change = [-1,0]
for idx, amount in enumerate(change):
point[idx]+=amount
return point
return None
else:
return None

recursive backtracking - python. not returning value

Problem
I am aware that somewhere in my function, I am not returning something I should.
I am returning the recursive call, but it appears that I am not returning "all the way out"
Context
I am doing a depth first search of every single combination in a list. Once I reach a combination that reaches a condition, I want to return.
I am maintaining "state" of my combination and am backtracking where I should be (I think).
What am I doing wrong?
class Combo:
def __init__(self, list):
self.staples = list
Combo has a property called "staples", consisting of a list of staple classes. I want to iterate over the list of staples in a decision tree to find an optimal number.
In this case, the optimal number is summed across the quantities of each staple instance in the list and stored/recalculated as a property on the Combo instance.
def IterateStaples(combo, target):
#Exit condition for combo dictionary
if all(combo.diff[macro] < 2 for macro in combo.diff):
return combo;
#iterate through all items in list
for staple in combo.staples:
#Increment and calc conditions
staple.increment()
combo.calcTotals()
combo.findDiff(target)
#If exceeds target value, backtrack
if combo.findConflict(target):
staple.decrement()
combo.calcTotals()
combo.findDiff(target)
#Redundant exit condition to try and return
elif all(combo.diff[macro] < 2 for macro in combo.diff):
return combo
#Recursive call
else:
return IterateStaples(combo, target)
staple.decrement()
combo.calcTotals()
combo.findDiff(target)
If I understand your code correctly (which is more difficult than usual, since you've not shown what most of the methods you're calling on combo and staple are), this should be what you want:
def IterateStaples(combo, target):
# base case
if all(combo.diff[macro] < 2 for macro in combo.diff): # iterate on combo.diff.values()?
return combo # returning combo indicates success!
for staple in combo.staples:
staple.increment() # update state
combo.calcTotals()
combo.findDiff(target)
if not combo.findConflict(target): # skip recursing on invalid states
result = IterateStaples(combo, target) # recursive case
if result is not None: # if the recursion was successful, return the result
return result
staple.decrement() # otherwise, undo the change to the state (backtrack)
combo.calcTotals() # these two lines might not be necessary when backtracking
combo.findDiff(target) # since other branches will call them after staple.increment()
return None # if we got to the end of the loop, explicitly return None to signal failure
The return None at the end is not strictly necessary, since None is the default return value if you don't return anything else. I just think that it's better to be explicit about it.
I'm following your code in returning combo on success (and extending it to returning None on failure). Since the code mutates combo in place, you could just as well return True for success (in the base case at the top of the function) and False for failure (at the bottom of the function, after the end of the loop). The recursive logic would pass on True results, and backtrack after False results. The top-level caller would need to check the combo instance they'd passed in for the actual solution if they got a True return value:
combo = Combo(something)
if IterateStaples(combo, target):
do_stuff(combo) # success!
Your first if statement inside the for loop doesn't return anything. What it should return depends on your algorithm's logic:
#If exceeds target value, backtrack
if combo.findConflict(target):
staple.decrement()
combo.calcTotals()
combo.findDiff(target)
return SOMETHING
Additionally, the last 3 lines won't ever get executed, they're after the return statement.
I was able to pass my own test case by incorporating a helper function in the following:
Is this not backtracking? I implemented N-queens with a similar approach
def IterateStaples(combo, target):
#iterate through all items in list
bestCombo = []
def helper(combo):
for staple in combo.staples:
#Increment and calc conditions
staple.increment()
combo.calcTotals()
combo.findDiff(target)
#If exceeds target value, backtrack
if combo.findConflict(target):
staple.decrement()
combo.calcTotals()
combo.findDiff(target)
#Redundant exit condition to try and return
elif all(combo.diff[macro] < 2 for macro in combo.diff):
bestCombo.append(deepcopy(combo))
return
#Recursive call
else:
helper(combo)
staple.decrement()
combo.calcTotals()
combo.findDiff(target)
helper(combo)
return bestCombo

Function doesn't stop after "return(False)"

I am trying to write a function which is supposed to compare list structures (the values are indifferent). The problem is that I have two lists which are unequal but the function still returns True even though it actually goes into the else part. I don't understand why and what I did wrong. Here is my code:
def islist(p): #is p a list
return type(p)==type(list())
def ListeIsomorf(a,b):
if len(a)==len(b):
for i,j in zip(a,b):
if islist(i) and islist(j):
ListeIsomorf(i,j)
elif islist(i) or islist(j):
return(False)
return(True)
else:
print(a,"length from the list isn't equal",b)
return(False)
#example lists
ListeE = [[],[],[[]]]
ListeD = [[],[],[[]]]
ListeF = [[[],[],[[]]]]
ListeG = [[],[[]],[[]]]
ListeH = [1,[3]]
ListeI = [1,3]
#tests
print(ListeIsomorf(ListeD,ListeE)) # True
print(ListeIsomorf(ListeD,ListeF)) # False
print(ListeIsomorf(ListeD,ListeG)) # False
print(ListeIsomorf(ListeH,ListeI)) # False
So the problem only occurs with the third print(ListeIsomorf(ListeD,ListeG)) # False. It actually goes into the else part and does print the "length from the list isn't equal" but it doesn't stop and it doesn't give out the return(False). Am I missing something?
The problem is that when your function calls itself recursively:
ListeIsomorf(i,j)
it ignores the returned value.
Thus the comparisons that take place at the second level of recursion have no effect on what the top level returns.
Changing the above to:
if not ListeIsomorf(i,j):
return(False)
fixes the problem.

Categories