Hey guys so i have a problem and it is that my return statement is not executing.
This is my sudo code. It searches a tree to see if an item is in the tree.
def search(self, i):
if left child is None and right child is None
if self.data == i:
return True
else:
pass
elif self.data == i:
return True
else:
if left child exists:
return self.left.search(i)
if right child exists:
return self.right.search(i)
The code seems to work except when self.data == i, the code does not run the return True statement even though the
if statement is executed. Does anyone know why this is?
Thanks in advance!
EDIT: ADDED THE SELF PARAMETER. IT WAS SUPPOSED TO BE THERE, WAS A TYPO..
I inserted the numbers 3, 8, 2 and 1 into the tree and then searched for 1. I added a print statement if left child is None and right child is None: if self.data == i: print('self.data == i') return True I added that print statement when searching for 1 and the print statement did print which means that the if statement was executed, however, the return True statement does not execute
I imagine you're trying to do a binary search, in which case you have some problems in your code. The return statement is probably not executed because the condition self.data == i fails. Also, note that if the root has a left child, the right child never gets looked at in your original code.
This is an implementation of a linear search, I'll leave it as an exercise to modify it to a binary search.
def search(self, i):
if self.data == i:
return True
if self.left is not None:
if self.left.search(i):
return True
if self.right is not None:
return self.right.search(i)
return False
Related
I am traversing the children of a node in a binary tree and checking the locked property. Return False if any of the children lock state is False and True otherwise. I can't seem to find the right way to place the return statement in the code. Note that the input to the lock function is a node object.
class BinaryTree():
def __init__(self, value, lock=False):
self.locked = lock
self.value = value
self.left = None
self.right = None
def lock(self, node_object):
"""lock a node if the descendants are locked using post-order traversing"""
flag = True
if node_object.left:
if node_object.left.locked == True:
print(
f'>>> Left child: {node_object.left.value}. Locked?: {node_object.left.locked} <<<')
self.lock(node_object.left)
else:
flag = False
print(
f'>>> Children Node: {node_object.left.value}\tstate: {node_object.left.locked}. Lock failed <<<')
if node_object.right:
if node_object.right.locked == True:
print(
f'>>> Right child: {node_object.right.value}. Locked?: {node_object.right.locked} <<<')
self.lock(node_object.right)
else:
flag = False
print(
f'>>> Children Node: {node_object.right.value}\tstate: {node_object.right.locked}. Lock failed <<<')
return flag
# test the functions
if __name__ == "__main__":
BT = BinaryTree(None)
count = 0
lock_state = False
nodes = [34, 2, 1, 6, 8, 9, 56, 99, 150, 45, 3]
for item in nodes:
BT.add_node(item, lock_state) # test add_node
node = BT.find_node(56) # test find_node function
if node is not None:
status = BT.lock(node)
print(status)
status is always True, even when the else statements are executed.
To get the recursion right, let's look at the base case:
If current node is unlocked, return False
If left exists and is unlocked, return False
If right exists and is unlocked, return False
Return True
The checks on left and right should be done recursively:
def is_locked(self):
return self.locked and (not self.left or self.left.is_locked()) and (not self.right or self.right.is_locked())
Since and and or are short-circuiting operators, only the trees that need to be checked are checked. Notice that we use the return values from the calls on the children, unlike your original code.
As per the comment, you can generalize this approach using the built-in all. While not especially useful for a binary tree, this approach generalizes well to arbitrary n-ary trees:
def is_locked(self):
return self.locked and all(node.is_locked() for node in (self.left, self.right) if node)
An n-ary tree would likely have a mutable sequence attribute self.children instead of (self.left, self.right).
Every new call to a recursive function puts a new instance of that function on a stack, with its own copies of local variables.
You're trying to manipulate flag as if it is some global variable, but it is local to each called instance of lock(). Note that this is a good thing - what if you had multiple objects, they certainly shouldn't share the same flag. And changing flag to an attribute of the class would still be problematic in some cases.
You're on the right track, trying to return a result, but when you make the recursive calls with self.lock(), those calls will return the result for that section and you should therefore capture the return value and deal with it, as others have suggested.
i.e. flag = self.lock(node_object.left) instead of just calling self.lock(node_object.left). I would suggest renaming flag to result, since that is really what it is: a variable that's holding the result.
As an improvement, instead of assigning the result to flag / result and returning it at the end - since you don't use flag anywhere except to return the result - you could just change all the flag = <something> statements to return <something> statements. There is no rule against having multiple return statements, although some purists may not like it for specific reasons.
Before asking, I searched out some old questions and get a better idea to put the "return" in front of the inside re-invocated the function to get the expected result.
some of them like:
How to stop python recursion
Python recursion and return statements. But when I do the same thing with my problem, it gets worse.
I have a Binary Search Tree and want to get the TreeNode instance by given a node's key, so it looks an easier traversal requirement and I already easily realized similar functions below, with which I did NOT put return in front of the function:
#preorder_List=[]
def preorder(treeNode):
if treeNode:
preorder_List.append(treeNode.getKey())
preorder(treeNode.has_left_child())
preorder(treeNode.has_right_child())
return preorder_List
so for my new requirement, I compose it like below first:
def getNode(treeNode,key):
if(treeNode):
if(treeNode.key==key):
print("got it=",treeNode.key)
return treeNode
else:
getNode(treeNode.left_child(),key)
getNode(treeNode.right_child(),key)
then the issue occurs, it finds the key/node but kept running and report a None error finally and then I put return in front of the both left and right branch like below:
def getNode(treeNode,key):
if(treeNode):
if(treeNode.key==key):
print("got it=",treeNode.key)
return treeNode
else:
return getNode(treeNode.left_child(),key)
return getNode(treeNode.right_child(),key)
but this makes the thing worse, it did reach the key found and return None earlier.
Then I tried to remove one "return" for the branch, no matter right or left. It works (Update: this worked when my test case contains only 3 nodes, when I put more nodes, it didn't work, or to say if the expected node is from right, then put return in front of right branch invocation works, for left one, it didn't). What's the better solution?
You need to be able to return the results of your recursive calls, but you don't always need to do so unconditionally. Sometimes you'll not get the result you need from the first recursion, so you need to recurse on the other one before returning anything.
The best way to deal with this is usually to assign the results of the recursion to a variable, which you can then test. So if getNode either returns a node (if it found the key), or None (if it didn't), you can do something like this:
result = getNode(treeNode.left_child(),key)
if result is not None:
return result
return getNode(treeNode.right_child(),key)
In this specific case, since None is falsey, you can use the or operator to do the "short-circuiting" for you:
return getNode(treeNode.left_child(),key) or getNode(treeNode.right_child(),key)
The second recursive call will only be made if the first one returned a falsey value (such as None).
Note that for some recursive algorithms, you may need to recurse multiple times unconditionally, then combine the results together before returning them. For instance, a function to add up the (numeric) key values in a tree might look something like this:
def sum_keys(node):
if node is None: # base case
return 0
left_sum = sumKeys(node.left_child()) # first recursion
right_sum = sumKeys(node.right_child()) # second recursion
return left_sum + right_sum + node.key # add recursive results to our key and return
Without knowing more about your objects:
Three base cases:
current node is None --> return None
current node matches the key --> return it
current node does not match, is the end of the branch --> return None
If not base case recurse. Short circuit the recursion with or: return the left branch if it a match or return the right branch result (which might also be None)
def getNode(treeNode,key):
if treeNode == None:
return None
elif treeNode.key == key:
print("got it=",treeNode.key)
return treeNode
elif not any(treeNode.has_left_child(), treeNode.has_right_child()):
return None
#left_branch = getNode(treeNode.left_child(),key)
#right_branch = getNode(treeNode.right_child(),key)
#return left_branch or right_branch
return getNode(treeNode.left_child(),key) or getNode(treeNode.right_child(),key)
Instead of return, use yield:
class Tree:
def __init__(self, **kwargs):
self.__dict__ = {i:kwargs.get(i) for i in ['left', 'key', 'right']}
t = Tree(key=10, right=Tree(key=20, left=Tree(key=18)), left=Tree(key=5))
def find_val(tree, target):
if tree.key == target:
yield target
print('found')
else:
if getattr(tree, 'left', None) is not None:
yield from find_val(tree.left, target)
if getattr(tree, 'right', None) is not None:
yield from find_val(tree.right, target)
print(list(find_val(t, 18)))
Output:
found
[18]
However, you could also implement the get_node function as a method in your binary tree class by implementing a __contains__ methods:
class Tree:
def __init__(self, **kwargs):
self.__dict__ = {i:kwargs.get(i) for i in ['left', 'key', 'right']}
def __contains__(self, _val):
if self.key == _val:
return True
_l, _r = self.left, self.right
return _val in [[], _l][bool(_l)] or _val in [[], _r][bool(_r)]
t = Tree(key=10, right=Tree(key=20, left=Tree(key=18)), left=Tree(key=5))
print({i:i in t for i in [10, 14, 18]})
Output:
{10: True, 14: False, 18: True}
I'm having trouble implementing a recursive depth-first search on a tree structure in python 2.7, could anyone point me in the right direction?
Code
class node(object):
def __init__(self, value,children=[]):
self.value=value
self.children=children
def __repr__ (self,level=0):
ret = "\t"*level+repr(self.value)+"\n"
for child in self.children:
ret +=child.__repr__(level+1)
return ret
def search(self,st):
if self.value==st:
return True
else:
if not self.children:
return False
else:
for child in self.children:
self.search(st)
sample data structure (for simple pasting):
tree = node("fashion",[
node("garment",[
node("t-shirt"),
node("shorts")]),
node("designer",[
node("Ralf"),
node("tommy"),
node("joe")])
])
EDIT: the problem is that I'm ending in an infinate loop when searching, for example:
tree.search("garment")
EDIT 2: update to fix the recursion not advancing (thanks Ben!), but not getting a False answer to tree.search("joe")
def search(self,st):
if self.value==st:
return True
else:
if self.children:
for child in self.children:
return child.search(st)
else:
return False
EDIT 3: astute observations from Sharth have led me to
def search(self,st):
if self.value==st:
return True
for child in self.children:
if child.search(st):
return True
return False
Which works great, but searching
>>>tree.search("p")
produces
Traceback (most recent call last): File "", line 1, in
tree.search("p") File "", line 15, in search
if child.search(st): AttributeError: 'list' object has no attribute 'search'
why is that happening?
EDIT4: works when using the above data structure, but fails with a third level of data, i.e.
tree = node("fashion",[
node("garment",[
node("t-shirt"),
node("shorts")]),
node("designer",[
node("Ralf"),
node("tommy"),
node("joe"),[
node("fresh")]
])
])
edit 5: messed up the braces in edit four.
In the following code, 3 issues have been fixed:
Mutable default arguments
When looping, we need to call child.search(st), not self.search(st).
We don't want to only look at the first child (as your 2nd edit does), we want to return success on the first one that is true. If the first one is false, we want to try the next..
So, the code:
class node(object):
def __init__(self, value, children=None):
self.value = value
self.children = children or []
def search(self, st):
if self.value == st:
return True
for child in self.children:
if child.search(st):
return True
return False
It looks like your search function isn't actually searching the children. Try this instead:
def search(self,st):
if self.value==st:
return True
else:
if not self.children:
return False
else:
return any(child.search(st) for child in self.children)
Notice that the last line has been modified to use a generator expression in conjunction with the built-in function any in order to search each of the children in sequence, stopping as soon as one of them results in a True.
The key difference here is that we call child.search(st) instead of self.search(st).
Also note that you really don't need a separate case for when children is empty, so you could do this instead:
def search(self,st):
if self.value==st:
return True
else:
return any(child.search(st) for child in self.children)
This can be further simplified:
def search(self,st):
return self.value==st or any(child.search(st) for child in self.children)
When you're returning a boolean literal it's a good sign that you can probably simplify by using and / or instead.
Please set children to None and not a list. Its very dangerous to have a mutable argument as a default parameter. See Hidden features of Python for reference. The value of children may not be an empty list, even if its intended to be.
I have a simple program above that creates a BST from a sorted array. It should parse the tree without showing the leaves which are essentially None. Could someone help explain why the program still spits out 'None'. I'm a python NooB and would appreciate any help.I have tried != 'None' along with is None but get the same results.
class Node:
def __init__(self,value):
self.value=value
self.nodeleft=None
self.noderight=None
def makeBST(ia,start,end,tree):
if (end < start):
return None
mid = (start + end) / 2
n = Node(ia[mid])
n.nodeleft = makeBST(ia, start, mid-1, tree)
n.noderight = makeBST(ia, mid+1, end, tree)
tree.append(n)
return n
def printBST(root):
print 'RR' ,root.value
if root.nodeleft == None:
print 'EOT'
else:
print printBST(root.nodeleft)
if root.noderight == None:
print 'EOT'
else:
print printBST(root.noderight)
if __name__ == '__main__':
array = [1, 2, 3, 4, 5, 6]
dic = []
root = makeBST(array, 0, len(array)-1, dic)
printBST(root)
The problem is that your code was passing the return value of printBST to print. Since printBST does not return anything, None was printed.
So when you wrote:
print printBST(root.nodeleft)
that code is certain to print None because printBST does not contain a return statement and so defaults to returning None.
You need to change printBST to do this:
def printBST(root):
print 'RR' ,root.value
if root.nodeleft is None:
print 'EOT'
else:
printBST(root.nodeleft)
if root.noderight is None:
print 'EOT'
else:
printBST(root.noderight)
Note also that using is is the correct way to test for None.
That said, you can make your code simpler like this:
def printBST(root):
if root is None:
print 'EOT'
return
print 'RR', root.value
printBST(root.nodeleft)
printBST(root.noderight)
As well as being simpler, this code has the additional benefit of not failing when presented with an empty tree.
printBST should return the values, and not print them. Because it does not return anything, it defaults to None. That is why printBST(root) is None
printBST(root) by itself won't print the value on its own. You have to put a print before:
print printBST(root)
Per PEP 8, you should never compare the NoneType singleton with equality operators (eg == and !=). Use is None and/or is not None
I'm currently working on XML (SVG) traversing and have the next problem:
my function always return None after if currNode.parentNode is rootNode:, though, as you can see in the code below, I've explicitly set return to 'flag', which is non-empty string.
The most interesting part here is that print('...') prints three dots, so the problem is exactly there (or at least looks like that).
According to the Python docs: 'If an expression list is present, it is evaluated, else None is substituted.'
So, in short, the question is:
why 'return' doesn't returns 'flag' string, but 'None' instead?
Here's this piece of code:
def relsLookup(currNode, rootNode):
if currNode.nextSibling is not None:
currNode = currNode.nextSibling
return currNode
elif currNode.nextSibling is None:
if currNode.parentNode is rootNode:
flag = 's2s_exception_end_of_svg'
print('...')
return flag
elif currNode.parentNode.localName == 'g':
if currNode.parentNode.nextSibling is not None:
print('<--')
currNode = currNode.parentNode.nextSibling
return currNode
elif currNode.parentNode.nextSibling is None:
print('<--')
currNode = currNode.parentNode
relsLookup(currNode, rootNode)
else:
flag = 's2s_exception_unknown_parent'
return flag
else:
flag = 's2s_exception_unknown_sibling'
return flag
I suspect the problem is with the following part of your code:
elif currNode.parentNode.nextSibling is None:
print('<--')
currNode = currNode.parentNode
relsLookup(currNode, rootNode)
Did you mean to say:
elif currNode.parentNode.nextSibling is None:
print('<--')
currNode = currNode.parentNode
return relsLookup(currNode, rootNode)
?
According to your listing, there is only one explanation: Either currNode.nextSibling isn't None or it is, but currNode.parentNode is rootNode is False. Set a default return statement at the end of the method / function to test this:
if currNode.nextSibling is None:
if currNode.parentNode is rootNode:
flag = 'end_of_svg'
print('...')
return flag
return 'nested if failed'
return 'currNode.nextSibling is not None!!!'
The core problem you are seeing is that a function which "falls off the end" (i.e. reaches the end of the code without returning a value or raising an exception) will implicitly return None to the caller.
Hence the suggestion from Daren to include an explicit return at the end to see what is happening and aix noting the recursive case where you don't actually return anything.
Given the structure of your code, the recursive case is currently broken, since it won't return a value.