patterncsharpMinor
Flatten a tree to a collection of nodes
Viewed 0 times
nodescollectiontreeflatten
Problem
I have implemented simple tree class as follows:
This works, but I have concerns about the following line:
Since I'm using
Is there some way I can refactor this to make the most of lazy evaluation, or am I better off leaving it this way?
public class MoveTreeNode
{
public MoveTreeNode Parent
{
get;
set;
}
private List children = new List();
public IEnumerable Children
{
get
{
return children;
}
}
public void Add(MoveTreeNode child)
{
child.Parent = this;
children.Add(child);
}
private IEnumerable Flatten()
{
yield return this;
foreach(var child in Children.Select(x=> x.Flatten()).SelectMany(x=>x))
{
yield return child;
}
}
}This works, but I have concerns about the following line:
foreach(var child in Children.Select(x=> x.Flatten()).SelectMany(x=>x))Since I'm using
yield I'd prefer to gain some of the benefits of lazy evaluation but I fear that while each child of the root will be lazily evaluated, the childrens' children, etc will be evaluated at once when their parents are evaluated.Is there some way I can refactor this to make the most of lazy evaluation, or am I better off leaving it this way?
Solution
I think you want to flatten each child in turn:
That way you won't flatten the whole tree first.
Edit:
It's more difficult to talk about things so let's look at some code. SelectMany is roughly equal to:
So as you can see, we will definitely get all of the nodes and if you stop iterating before the end (e.g. by calling
private IEnumerable Flatten()
{
yield return this;
foreach (var node in Children.SelectMany(child => child.Flatten()))
{
yield return node;
}
}That way you won't flatten the whole tree first.
Edit:
It's more difficult to talk about things so let's look at some code. SelectMany is roughly equal to:
public static IEnumerable SelectMany(this IEnumerable source, Func> selector)
{
foreach (var item in source)
{
foreach (var subItem in selector(item))
{
yield return subItem;
}
}
}So as you can see, we will definitely get all of the nodes and if you stop iterating before the end (e.g. by calling
.First()) you will only call Flatten() on a single child.Code Snippets
private IEnumerable<MoveTreeNode> Flatten()
{
yield return this;
foreach (var node in Children.SelectMany(child => child.Flatten()))
{
yield return node;
}
}public static IEnumerable<TOut> SelectMany<T, TOut>(this IEnumerable<T> source, Func<T, IEnumerable<TOut>> selector)
{
foreach (var item in source)
{
foreach (var subItem in selector(item))
{
yield return subItem;
}
}
}Context
StackExchange Code Review Q#87428, answer score: 6
Revisions (0)
No revisions yet.