Question

I am trying to develop a reactive DFS implementation that produces distinct nodes, without traversing unnecessary paths. The tree may contain duplicate/equal nodes. If a node has been traversed, then all of it's children have been traversed as well, hence we can stop expanding the traversal if we re-encounter a node.

class Traversal {
  //exposed method with understandable signature
  public IObservable<Node> DepthFirst(Node root) {
    return DepthFirstRecursive(root, new List<Node>(), new SomeStatefulHelper());
  }

  //hidden method with accumulator for cutting
  private IObservable<Node> DepthFirstRecursive(Node root, List<Node> accuFilter, SomeHelper helper) {
    return root.GetChildren().AsObservable()

      //here we cut traversal:
      .Where(n => !accuFilter.Contains(n))

      //cannot do SelectMany, because we want sequential processing:
      .Select(n => DepthFirstRecursive(n, accuFilter, helper)).Concat()
      .Concat(Observable.Return(root))

      //adding results to accumulator
      .Do(accuFilter.Add)
  }
}

class Node : IEquatable<Node> {
  public IEnumerable<Node> GetChildren(SomeHelper helper) {
    //produce child nodes
  }
  ...
}

The accumulator is not pretty. It's basically a side effect woven through the otherwise elegant reactive implementation. How can I get rid of it using Rx? Observable.Distinct() is not going to cut it (pun intended), since that can only operate at the very end of the chain (where the .Do is).

Also, with the helper I'm getting an "Implicitly captured closure: helper" warning on the => in the .Where. I don't see that this warning is relevant. Is it? How do I get rid of it?

Was it helpful?

Solution

I don't think RX is the right tool for the job here.

Also, you should use a HashSet to keep track of the visited nodes.
List.Contains() iterates over all elements, so you will introduce O(n^2) behavior.

To flatten the result it's easiest to use Enumerable.SelectMany()

Instead of relying on a extra class to implement GetChildren() you can simply use a delegate Func<T, IEnumerable<T>> getChildren that specifies how to get the children of the node type in question.

This is how I would do it:

    public static IEnumerable<T> DepthFirst<T>(T node, 
                                               Func<T, IEnumerable<T>> getChildren, 
                                               HashSet<T> visitedNodes)
    {
        return getChildren(node)
              .Where(visitedNodes.Add)
              .SelectMany(child => DepthFirst(child, getChildren, visitedNodes))
              .Concat(new[] { node });
    }

    public static IEnumerable<T> DepthFirst<T>(T root, 
                                               Func<T, IEnumerable<T>> getChildren) 
    {
        return DepthFirst(root, getChildren, new HashSet<T> {root});
    }
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top