Warning: file_get_contents(/data/phpspider/zhask/data//catemap/3/reactjs/22.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 如何对具有循环依赖项的项进行递归_C#_Recursion_Cyclic - Fatal编程技术网

C# 如何对具有循环依赖项的项进行递归

C# 如何对具有循环依赖项的项进行递归,c#,recursion,cyclic,C#,Recursion,Cyclic,我正在寻找一种更好的方法来递归可能具有循环依赖项的项。目前,我传递了一个已经处理的项目列表,以便不再处理它们,但这可能不是最好的方法 以下是我目前正在做的事情: // ///缓存依赖项以提高性能 /// 私有静态只读IDictionary依赖项 =新字典(); /// ///递归查找此oi所依赖的OwnedItem ///为了正确处理循环依赖关系,已经考虑了 ///还需要提供依赖项(可以为null或空) /// /// /// /// 私有静态IEnumerable GetDepe

我正在寻找一种更好的方法来递归可能具有循环依赖项的项。目前,我传递了一个已经处理的项目列表,以便不再处理它们,但这可能不是最好的方法

以下是我目前正在做的事情:


//
///缓存依赖项以提高性能
/// 
私有静态只读IDictionary依赖项
=新字典();
/// 
///递归查找此oi所依赖的OwnedItem
///为了正确处理循环依赖关系,已经考虑了
///还需要提供依赖项(可以为null或空)
/// 
/// 
/// 
/// 
私有静态IEnumerable GetDependencies(
OwnedItem oi,
IEnumerable parentDeps)
{
if(null==oi)
{
返回可枚举的.Empty();
}
if(dependencies.ContainsKey(oi.UniqueId))
{
返回依赖项[oi.UniqueId];
}
var comparer=新的TCObjectComparer();
var结果=新哈希集(比较器);
结果:添加(oi);
result.UnionWith(parentDeps??Enumerable.Empty());
foreach(oi.allusedowneditems中的oi2变量除外(
结果(比较器)
{
UnionWith(GetDependencies(oi2,result));
}
依赖项[oi.UniqueId]=结果;
返回结果;
}

这些项目属于“OwnedItem”类型,并在属性中保留其直接依赖项的列表(
IEnumerable
AllusedOwnEditemStobeInclude
,但基本上,只要“项目”保留循环依赖项可能发生的“项目”列表,就应该采用这种方法。 使用字典只需避免多次进行相同的计算;这不是必要的。此外,只需要
TCObjectComparer
的一个实例,但这也不是必需的。
有什么建议吗?我想一定存在一些经典算法来处理这个问题,但我找不到它。

你要做的基本上是遍历一个连通图的所有节点。AllUsedOwnedItemsToBeIncluded属性是连接到当前节点的节点列表

你可以看看这里,找到一些可能有用的

算法是执行图遍历的一种方法。您必须检查每个节点,并保留已访问节点的列表,以避免访问他两次

减少遍历次数的另一种算法可以是:

list nodesToExplore;
list exploredNodes;
nodesToExplore.add(startNode);

for all node in nodesToExplore
{
    nodesToExplore.remove(node);
    exploredNodes.add(node);

    for all child in node.childs
    {
        if(child not in exploredNodes)
           nodesToExplore.add(child);
    }
}

当它结束时,exploredNodes将包含您需要的内容。使用hashset/dictionnary而不是list将提高性能

算法可以被提取到类中,从而使代码更整洁,并摆脱臭烘烘的静态字段

private static IEnumerable<T> GetDependencies(T oi)
{
    return new FlattenedCircularTree<OwnedItem>(oi, o => o.AllUsedOwnedItemsToBeIncluded)
       .AllNodes();
}
private静态IEnumerable GetDependencies(TOI)
{
返回新的扁平化循环树(oi,o=>o.Allusedowneditemstobeinclude)
.AllNodes();
}
一般算法是这样实现的:

public sealed class FlattenedCircularTree<T>
{
    private readonly T _root;
    private readonly Func<T, IEnumerable<T>> _getChildren;
    private readonly HashSet<T> _visited = new HashSet<T>();
    private readonly List<T> _nodes = new List<T>();

    public FlattenedCircularTree(T root, Func<T, IEnumerable<T>> getChildren)
    {
        _root = root;
        _getChildren = getChildren;
    }

    public IEnumerable<T> AllNodes()
    {
        FindNodes(_root);
        return _nodes;
    }

    private void FindNodes(T current)
    {
        if (!_visited.Add(current))
            return;
        _nodes.Add(current);
        IEnumerable<T> children = _getChildren(current);
        if (children != null)
            foreach (T child in children)
                FindNodes(child);
    }
}
公共密封类扁平化循环树
{
私有只读T_根;
私有只读函数_getChildren;
private readonly HashSet_visted=new HashSet();
私有只读列表_节点=新列表();
公共扁平循环树(T根,Func getChildren)
{
_根=根;
_getChildren=getChildren;
}
公共IEnumerable AllNodes()
{
FindNodes(_根);
返回_节点;
}
专用无效FindNodes(T当前)
{
如果(!\u已访问。添加(当前))
返回;
_节点。添加(当前);
IEnumerable children=\u getChildren(当前);
如果(子项!=null)
foreach(儿童中的T儿童)
FindNodes(儿童);
}
}

您可以实现如下功能:

  public static partial class LinqGraph {
    public static IEnumerable<T> SelectBreadthFirst<T>(this IEnumerable<T> source, 
                                                       Func<T, IEnumerable<T>> children) {
      if (Object.ReferenceEquals(null, source))
        throw new ArgumentNullException(nameof(source));
      else if (Object.ReferenceEquals(null, children))
        throw new ArgumentNullException(nameof(children));

      HashSet<T> proceeded = new HashSet<T>();

      Queue<IEnumerable<T>> queue = new Queue<IEnumerable<T>>();

      queue.Enqueue(source);

      while (queue.Count > 0) {
        IEnumerable<T> src = queue.Dequeue();

        if (Object.ReferenceEquals(null, src))
          continue;

        foreach (var item in src) 
          if (proceeded.Add(item)) {
            yield return item;

            queue.Enqueue(children(item));
          }
      }
    }
  } 

下面是我对文森特算法的实现:


专用静态只读TobjectComparer
=新的TCObject Comparer();
/// 
///缓存依赖项以提高性能
/// 
私有静态只读IDictionary依赖项
=新字典();
/// 
///递归查找此oi所依赖的OwnedItems
///看http://stackoverflow.com/questions/37614469/how-to-recurse-over-items-having-cyclic-dependencies
/// 
/// 
/// 
私有静态IEnumerable GetDependencies(OwnedItem oi)
{
if(null==oi)
{
返回可枚举的.Empty();
}
if(dependencies.ContainsKey(oi.UniqueId))
{
返回依赖项[oi.UniqueId];
}
var resultExploredNodes=新哈希集(比较器);
var nodesToExplore=新队列();
nodesToExplore.Enqueue(oi);
而(nodesToExplore.Count>0)
{
var node=nodesToExplore.Dequeue();
结果ploredNodes.Add(节点);
//将尚未访问的节点添加到nodesToExplore
node.allusedowneditemstobeinclude
.Exception(resultExploredNodes,比较器)
.ForEach(n=>nodesToExplore.Enqueue(n));
}
依赖项[oi.UniqueId]=resultExploredNodes;
返回resultExploredNodes;
}


同样,缓存只是为了提高性能,而不是算法所必需的。

请格式化代码示例,很难阅读。从描述中我可以看出,您的想法是正确的。但是代码很难理解,我不知道你是否做得正确。如果你发布了一个。我希望能够复制和运行您的代码。目前没有关于
OwnedItem
TCObjectComparer
的定义。没有样本数据和预期输出。@Nick Bailey-除了缩进的
///summary
行之外,它看起来与我的浏览器@weston中的Visual Studio中的情况非常相似-在某个地方有一个bug,但我不会去
  public static partial class LinqGraph {
    public static IEnumerable<T> SelectBreadthFirst<T>(this IEnumerable<T> source, 
                                                       Func<T, IEnumerable<T>> children) {
      if (Object.ReferenceEquals(null, source))
        throw new ArgumentNullException(nameof(source));
      else if (Object.ReferenceEquals(null, children))
        throw new ArgumentNullException(nameof(children));

      HashSet<T> proceeded = new HashSet<T>();

      Queue<IEnumerable<T>> queue = new Queue<IEnumerable<T>>();

      queue.Enqueue(source);

      while (queue.Count > 0) {
        IEnumerable<T> src = queue.Dequeue();

        if (Object.ReferenceEquals(null, src))
          continue;

        foreach (var item in src) 
          if (proceeded.Add(item)) {
            yield return item;

            queue.Enqueue(children(item));
          }
      }
    }
  } 
  var items = new OwnedItem[] {startItem} // root nodes
    //TODO: provide a rule of returning children on given parent
    .SelectBreadthFirst(parent => parent.AllUsedOwnedItemsToBeIncluded); 
    private static readonly TCObjectComparer<OwnedItem> comparer
        = new TCObjectComparer<OwnedItem>();

    /// <summary>
    /// caching dependencies in order to increase performance
    /// </summary>
    private static readonly IDictionary<string, IEnumerable<OwnedItem>> dependencies
        = new Dictionary<string, IEnumerable<OwnedItem>>();

    /// <summary>
    /// recursively find OwnedItems this oi depends upon
    /// see http://stackoverflow.com/questions/37614469/how-to-recurse-over-items-having-cyclic-dependencies
    /// </summary>
    /// <param name="oi"></param>
    /// <returns></returns>
    private static IEnumerable<OwnedItem> GetDependencies(OwnedItem oi)
    {
        if (null == oi)
        {
            return Enumerable.Empty<OwnedItem>();
        }
        if (dependencies.ContainsKey(oi.UniqueId))
        {
            return dependencies[oi.UniqueId];
        }
        var resultExploredNodes = new HashSet<OwnedItem>(comparer);
        var nodesToExplore = new Queue<OwnedItem>();
        nodesToExplore.Enqueue(oi);
        while (nodesToExplore.Count > 0)
        {
            var node = nodesToExplore.Dequeue();
            resultExploredNodes.Add(node);
            // add nodes not already visited to nodesToExplore
            node.AllUsedOwnedItemsToBeIncluded
                .Except(resultExploredNodes, comparer)
                .ForEach(n => nodesToExplore.Enqueue(n));
        }
        dependencies[oi.UniqueId] = resultExploredNodes;
        return resultExploredNodes;
    }