Python代码列出依赖项,避免循环

Python代码列出依赖项,避免循环,python,recursion,functional-programming,Python,Recursion,Functional Programming,假设您有一个描述项目依赖关系的字典,大致如下: deps = { 'A': ['B', 'C', 'D'], 'B': ['C', 'E'], 'C': ['D', 'F'], 'D': ['C', 'G'], 'E': ['A'], 'H': ['N'], } 这意味着项目“A”依赖于项目“B”、“C”和“D”等。显然,这可能具有任意复杂性 如何编写一个函数get_all_deps(item),该函数提供item的所有依赖项的列表,没有重复项,

假设您有一个描述项目依赖关系的字典,大致如下:

deps = {
    'A': ['B', 'C', 'D'],
    'B': ['C', 'E'],
    'C': ['D', 'F'],
    'D': ['C', 'G'],
    'E': ['A'],
    'H': ['N'],
}
这意味着项目“A”依赖于项目“B”、“C”和“D”等。显然,这可能具有任意复杂性

如何编写一个函数
get_all_deps(item)
,该函数提供
item
的所有依赖项的列表,没有重复项,也没有
item
。例如:

> get_all_deps('H')
['N']
> get_all_deps('A')
['B', 'C', 'D', 'E', 'F', 'G']
> get_all_deps('E')
['A', 'B', 'C', 'D', 'F', 'G']

我在寻找简洁的代码——理想情况下是一个递归函数。性能对于我的用例并不十分重要-我们讨论的是相当小的依赖关系图(例如几十个项)

您可以使用堆栈/待办事项列表来避免递归实现:

deps = {
    'A': ['B', 'C', 'D'],
    'B': ['C', 'E'],
    'C': ['D', 'F'],
    'D': ['C', 'G'],
    'E': ['A'],
    'H': ['N'],
}

def get_all_deps(item):
    todo = set(deps[item])
    rval = set()
    while todo:
        subitem = todo.pop()
        if subitem != item:  # don't add start item to the list
            rval.add(subitem)
            to_add = set(deps.get(subitem,[]))
            todo.update(to_add.difference(rval))
    return sorted(rval)

print(get_all_deps('A'))
print(get_all_deps('E'))
print(get_all_deps('H'))
结果:

['B', 'C', 'D', 'E', 'F', 'G']
['A', 'B', 'C', 'D', 'F', 'G']
['N']
todo
集合包含要处理的元素

  • 弹出一个元素并将其放入返回值列表中
  • 循环直到没有更多元素(好的,这里有一个循环)
  • 如果元素不在返回值中,则只添加要处理的元素
  • 返回排序列表

set
差异避免了循环依赖的问题,并且避免了“最大递归深度”。唯一的限制是系统内存。

哦,就像依赖项的传递闭包一样?你知道你的例子中存在依赖循环吗?@depperm我已经做了几次尝试,并且有不止一个有效的解决方案-所有这些都像黑客一样。我最后使用的方法只是对递归深度设置了一个任意的人工限制。一定有更好的解决方案。这是一个图形搜索问题。你有一个循环图。再次强调,研究广度优先搜索。你也可以看看Dijkstra的算法来了解避免循环的一个经典例子:你必须跟踪你访问过的节点。@JordanDimov问题不是人们没有阅读你的问题,问题是你有一个期望(相当标准)来显示你的尝试和你被卡住的地方。选择你的“最简单的”解决方案,并准确地解释你认为哪些不适合你的目的。您已经说过性能并不重要,因此,制定一个与之相关的简洁性基准实际上是您问题的关键部分。太好了,谢谢。我尝试了一个递归变量(例如,传递一个“seen”集合来记录递归),但没有成功。。堆叠技巧很棒。我仍然在想,是否有一种聪明的方法可以递归地实现这一点。