我应该如何解决 "flat" 数据结构中的逻辑依赖关系?

How should I resolve logic dependencies I have in a "flat" data structure?

我目前有一个 Dictionary<string, List<string> 或依赖项。

键是属性名称,值是依赖属性列表。

这是一个简单的例子:

{key: "a", value: ["b", "d"]},
{key: "b", value: null},
{key: "c", value: ["a"]},
{key: "d", value: ["b", "e", "f"]},
{key: "e", value: null},
{key: "f", value: null}

first 级别依赖项如下所示:

a -> b,d
b -> nothing
c -> a
d -> b,e,f
e -> nothing
f -> nothing

找到第一级依赖关系后,second 级依赖关系将如下所示:

a -> b,d,e,f (d has a dependency to e and f).
b -> nothing
c -> a,b,d (a has a dependency to b and d).
d -> b,e,f
e -> nothing
f -> nothing

third 和最后一级(在 这个 案例中)看起来像:

a -> b,d,e,f
b -> nothing
c -> a,b,d,e,f
d -> b,e,f
e -> nothing
f -> nothing

现在我的词典比我在这个例子中显示的条目多得多(超过一千)。

我的想法是做一些类似于 angularjs 的摘要。我会创建一个阈值,比如说 10 或 20 运行s。在每个 运行 中,我将检查每个属性依赖项并更新模型。如果至少有一次更新,则执行递归调用。

我很确定这是一个 "known problem" 更好的方法。

无论如何:

  1. 我该如何解决这个问题?
  2. 我如何知道我需要迭代的级别数,直到我不再有依赖项。
  3. 我如何知道是否存在循环依赖?我有很多数据,很难分析?

你只需要写几个递归算法。看这里:

internal static class Program
{
    private static Dictionary<string, List<string>> _dependencies;

    static void Main(string[] args)
    {
        _dependencies = new Dictionary<string, List<string>>
        {
            {"a", new List<string>{"b","d"}},
            {"b", new List<string>()},
            {"c", new List<string>{"a"}},
            {"d", new List<string>{"b","e","f"}},
            {"e", new List<string>()},
            {"f", new List<string>()}
        };
        Console.WriteLine(string.Join(",", DeepWalkDependencies("a", new HashSet<string>())));
        Console.ReadLine();
    }

    static IEnumerable<string> DeepWalkDependencies(string key, HashSet<string> alreadyVisitedKeys)
    {
        foreach (var d in _dependencies[key])
        {
            if (alreadyVisitedKeys.Add(d))
            {
                yield return d;
                foreach (var dd in DeepWalkDependencies(d, alreadyVisitedKeys))
                {
                    yield return dd;
                }
            }
        }
    }
}

检测循环依赖可以类似地实现。您需要 List<string> currentPath 而不是 HashSet<string> alreadyVisitedKeys ,它将在每次调用递归函数之前重新创建。实现这个算法是一个很好的练习。享受吧!