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
Related
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? :)
The problem is my code keeps reflecting a variable as zero and this is caused by the fact that the variable is assigned at the start of my function, so each time I call the function the value evaluates to zero. However I need this variable assignment for the code to work and placing it within the elif statements still evaluates to zero and placing the the variable outside the function causes the function not work.
The aim of the program is to count pairs of consecutive letters in a string using recursion with no for/while loops in the code.
def countpairs(s):
pairs=0
if len(s)<2:
return 0 #base case
elif s[0].lower() == s[1].lower(): #recursion
pairs=+1
return countpairs(s[1:])
else: #recursion
pairs=+0
return countpairs(s[1:])
print(countpairs('Hello Salaam'))
This code is supposed to evaluate to 2 because of "ll" and "aa".
You need to wrap your head a little around what the recursion will do: it will call the function recursively to count the pairs from that point on, then (it should) add the pair, if any, found by this instance.
So your function needs to do something with the result of the recursive call, not just return it unchanged. For example, instead of this
elif s[0].lower() == s[1].lower():
pairs = +1
return countpairs(s[1:])
you might write this:
elif s[0].lower() == s[1].lower():
return countpairs(s[1:]) + 1
Something along these lines. You'll need to do a bit more work to get it just right, but I hope you get the idea.
The problems is that the variable pairs resets every recursive call...
When using counting recursive algorithms you don't need a variable that counts, that's the beauty
Instead, try to think how to recursive call can help you count.
def countpairs(s):
if len(s)<2:
return 0
elif s[0].lower() == s[1].lower():
return countpairs(s[1:])+1
else:
return countpairs(s[1:])
print(countpairs('Hello Salaam'))
There you go, in the recursive call the "counter" gets bigger every time it should be, think of the counter as a part of the function stack (or something like that).
You need to fix the syntax: pairs=+1 should be pairs+=1, same for pairs=+0. And you can pass the total into the next level.
def countpairs(s, pairs=0):
if len(s)<2:
return pairs #base case
elif s[0].lower() == s[1].lower(): #recursion
pairs+=1
return countpairs(s[1:], pairs)
else: #recursion
pairs+=0
return countpairs(s[1:], pairs)
print(countpairs('Hello Salaam')) # 2
You can do it by creating a recursive nested function and defining pairs in the outer function. Here's what I mean (with fixes for other issues encountered):
def countpairs(s):
pairs = 0
def _countpairs(s):
nonlocal pairs # Since it's not local nor global.
if len(s) < 2: # base case
return pairs
elif s[0].lower() == s[1].lower():
pairs += 1
return _countpairs(s[1:]) # recursion
else:
return _countpairs(s[1:]) # recursion
return _countpairs(s)
print(countpairs('Hello Salaam')) # -> 2
The code will always evaluate to zero because the last recursion will always have the length of s being less than 2. Instead use the global keyword to be able to grab the value of pairs.
numberOfPairs = 0
pairsList = []
def countpairs(s):
global numberOfPairs
if len(s)<2:
print("doing nothing")
return 0 #base case
elif s[0].lower() == s[1].lower(): #recursion
numberOfPairs+=1
newString = f"{s[0]} is equal to {s[1]}"
print(newString)
pairsList.append(newString)
return countpairs(s[1:])
else:
print(f"nothing happened: {s[0]}") #recursion
return countpairs(s[1:])
print(f"\nThe returned value of countpairs is: {countpairs('Hello Salaam')}")
print(f"Number of pairs: {numberOfPairs}")
print(pairsList)
I have a branching nested dictionary to visualize species taxonomy data. I'm trying to write a function that gives me all the branches at a particular level.
I've tried iterative and recursive functions, but I have only gotten close using a recursive function.
However, depending on where I put return/print statements, my function either returns None (but prints the correct information), or returns only one branch of the data.
Using the second option, the output is perfect until the dataset branches.
tree = {"k-b":
{"p-a":
{"c-a":{"o-a":{}, "o-b":{}},
"c-b":{"o-a":{}}},
"p-b":
{"c-a":{"o-a":{},"o-b":{}}}}}
def branches(tree, level):
if level == 0:
#print(tree.keys())
return tree.keys()
else:
for i in tree.keys():
return branches(tree[i], level-1)
print(branchNumber(tree, 2))
For level 2, I expect [['c-a', 'c-b'], ['c-a']] (it doesn't have to be an array of arrays, and I don't care if it has dict_keys() or anything else around it)
I actually get dict_keys(['c-a', 'c-b']), which excludes the second branch
Alternatively, if I remove the 'return' before recursively calling branches, and uncomment the print statement, it prints:
dict_keys(['c-a', 'c-b'])
dict_keys(['c-a'])
Which is exactly the output I want, but the function returns None so I can't store that information for future applications
Your code always returns the first item in the loop, so your algorithm ends prematurely and doesn't explore all the necessary branches. You could yield the results to create a generator function (among other approaches):
tree = {"k-b":
{"p-a":
{"c-a":{"o-a":{}, "o-b":{}},
"c-b":{"o-a":{}}},
"p-b":
{"c-a":{"o-a":{},"o-b":{}}}}}
def branches(tree, level):
if level == 0:
yield list(tree.keys())
elif level > 0:
for v in tree.values():
yield from branches(v, level - 1)
for i in range(4):
print(f"level {i}:", list(branches(tree, i)))
Output:
level 0: [['k-b']]
level 1: [['p-a', 'p-b']]
level 2: [['c-a', 'c-b'], ['c-a']]
level 3: [['o-a', 'o-b'], ['o-a'], ['o-a', 'o-b']]
The line elif level > 0: is an optimization to avoid walking deeper into the tree than necessary.
Also, for i in tree.keys(), then tree[i] to access the value could be clearer as for v in tree.values().
You might want to return a list of all items at that level:
tree = {"k-b":
{"p-a":
{"c-a":{"o-a":{}, "o-b":{}},
"c-b":{"o-a":{}}},
"p-b":
{"c-a":{"o-a":{},"o-b":{}}}}}
def branches(tree, level):
if level == 0:
#print(tree.keys())
return tree.keys()
else:
return [branches(tree[i], level-1) for i in tree.keys()]
print(branches(tree, 2))
Output:
[[dict_keys(['c-a', 'c-b']), dict_keys(['c-a'])]]
It sounds like you want to return a list of all branches. One way to do this is with a list comprehension:
def branches(tree, level):
if level == 0:
#print(tree.keys())
return tree.keys()
else:
return [branches(tree[i], level-1) for i in tree.keys()]
Note that this will return a deeply nested list. Flattening is left as an exercise for the reader.
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))
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]