问题
I'm having problems with traversing all descendants of an object.
The 'unit'
in the code below is of type Unit
in my program. It has a property ChildUnits
which returns a List<Unit>
of the children of the unit.
I can successfully perform operations on the children. Then I check if those children have children, and if they do I can perform operations on them as well.
However, I need to check all descendants in case there is more depth than just grandchildren. I had a go with while loops in addition to the code below but it got really messy so I left it out.
This is the code I have reverted back to:
foreach (var child in unit.ChildUnits)
{
//do something here with the child (I know it sounds dodgy).
bool hasMoreChildren = child.ChildUnits.Count != 0;
if(hasMoreChildren)
{
foreach (var descendant in child.ChildUnits)
{
//do something here with the descendant.
}
}
}
I could just go another level deep as it's relatively rare for a unit to have more depth than that. But that's not a clean solution.
I think I might need to use a graph traversal algorithm and/or recursion perhaps, but I would like some advice on how to solve this problem most efficiently.
Edit: Is it possible to do this without defining a new function/method?
回答1:
Edit: Is it possible to do this without defining a new function/method?
You could use an anonymous method...which is not exactly "not defining a new method", I know :)
However, there's another issue you should take care of: Circular references... even if you dont think there will be any
Here's an implementation, without defining any new method
Action<IEnumerable<Unit>> process = null;
var processed = new HashSet<Unit>();
process = list => {
foreach(var u in list.Where (processed.Add))
{
// do something here with u
//... and then process children
process(u.ChildUnits);
}
};
process(myList); // do the actual processing
回答2:
Algorithm like this:
def traverse(Unit i):
for (Unit child : i.childList):
// Perform your logic for child
traverse(child)
This will perform the same function for each child for the first node , and when applying it for i.child[j] it will perform the same function for all i.child[j].child[k] so it will perform what you want for each node and all its childs.
instead you can use stack :
stack s;
s.push(firstNode);
while(!stack.empty()):
t = stack.pop()
foreach(Unit child : t):
s.push(child)
// Perform logic for child
回答3:
You can use recursion:
void processChildren(List<Unit> children)
{
foreach (var child in children)
{
//do something here with the child (I know it sounds dodgy).
processChildren(child.Children); // recursive call here
}
}
If you don't want to define a new method, you could also roll your own stack:
var stack = new Stack<Unit>();
stack.push(firstUnit);
while( !stack.Any() ) {
var item = stack.pop();
//do something here with the item
foreach(var child in item.Children)
{
stack.push(child);
}
}
回答4:
Another way to do this without recursion or an action/lambda, is to use a list with items to handle.
Like this.
var toDoList = new List<Unit> { unit };
while (toDoList.Any()) {
// Get current child, and remove it from the to-do-list
var currentChild = toDoList.First();
toDoList.RemoveAt(0);
// Do something with the current child.
// ...
// Now see, if the child has any children to handle
if (currentChild.ChildUnits != null && currentChild.ChildUnits.Any()) {
toDoList.AddRange(currentChild.ChildUnits);
}
}
来源:https://stackoverflow.com/questions/25304000/traversing-all-descendants-of-an-object