I'm trying to write code that will take an object that can contain an arbitrarily large number of nested objects, each of which can contain nested objects, and so on. I'm trying to write a function that will find the first object that matches a given criteria. I don't care about matching all objects.
Python 2 is the language here, but this theory should reach into any language.
class MyObject:
myType = ""
myChildren = list([])
def __init__(self, t):
self.myType = t
a = MyObject("top")
a.myChildren.append(MyObject("obj1"))
a.myChildren.append(MyObject("obj2"))
a.myChildren.append(MyObject("obj3"))
a.myChildren[1].myChildren.append(MyObject("objA"))
a.myChildren[1].myChildren.append(MyObject("objB"))
a.myChildren[1].myChildren.append(MyObject("objC"))
a.myChildren[1].myChildren[0].myChildren.append(MyObject("Sam"))
a.myChildren[1].myChildren[0].myChildren.append(MyObject("Max"))
a.myChildren[1].myChildren[0].myChildren.append(MyObject("Waldo"))
a.myChildren[1].myChildren[1].myChildren.append(MyObject("Adam"))
a.myChildren[1].myChildren[1].myChildren.append(MyObject("Waldo"))
a.myChildren[2].append("Waldo"))
At this point we should have a recursion tree that looks something like this:
[top]
|-- [obj1]
|-- [obj2]
| |-- [objA]
| | |-- [Sam]
| | |-- [Max]
| | \-- [Waldo]
| |-- [objB]
| | |-- [Adam]
| | \-- [Waldo]
| \-- [objC]
\-- [obj3]
\-- [Waldo]
So now, I want to write a function that will find and return a reference to the first instance of a Waldo. As you can see, there might be more than one Waldo. I'd only be interested in the top/obj2/objA/Waldo instance.
The only recursive code I've done in the past uses recursive returns to return progressively more content. For example: a function that will create a recursion tree:
def printTree(rootObj,indentLevel=0):
output = "%s%s\n" % (" "*indentLevel, rootObj.myType)
for obj in rootObj:
output += printTree(obj,indentLevel+1)
return output
The thing is, as I said, I only want the first instance, and I want to stop walking the tree at that point. Walking the entire tree would be HUGELY inefficient once the trees get large and the desired object is somewhere early in the tree.
I can't seem to figure out how the logic to make this happen would work. My initial thought was something like this:
def findFirst(rootObj, desiredType):
# test this object first
if (rootObj.myType == desiredType):
return rootObj
# we are not the correct object, so now test all sub-objects.
for (obj in rootObj):
if (findFirst(obj,desiredType) is not None):
return obj
return None # nothing was found
However this doesn't quite work. As is it's just returning the first object even though it doesn't match.
Could someone help shed some light on the logic that would be needed to accomplish this?
EDIT: some clarification. The method of recursion is that we will descend as deep as possible into the tree and then work our way back out for each object. The list is also ordered. So for example, the path of recursion for the above tree would be:
top -> obj1 -> obj2 -> objA -> Sam -> Max -> Waldo -> objB -> Adam -> Waldo -> objC -> obj3 -> Waldo
Additionally, I did give an example of a self-created object, however, the objects in the application I want to use this code in are dynamically created; thus, serializing or converting the object tree would end up walking the entire tree anyway, which slows things down since we're generating dynamic objects based on state.
A good way of looking at what I'm looking for is a recursive file search function. Let's say we have a folder on disk and we want the first file, anywhere in that folder, that matches a given filename based on a regular expression and has a size within a given criteria. (This means we have to do computations on each object, we can't simply just check variables and filter based on them.) However, we don't want to parse the entire drive's directory structure if that file is in the root directory or in the first directory we come to. The real spirit of my question is "how can you escape out of this recursive loop as soon as you find something that matches your desired criteria?"