【问题标题】:Python code to list dependencies, avoiding loops列出依赖项的 Python 代码,避免循环
【发布时间】:2017-05-24 01:04:04
【问题描述】:

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

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']

我正在寻找简洁的代码 - 理想情况下是单个递归函数。性能对我的用例来说并不是很重要——我们谈论的是相当小的依赖图(例如几十个项目)

【问题讨论】:

  • 哦,像依赖的传递闭包?
  • 你知道你的例子中有一个依赖循环吗?
  • @depperm 我已经进行了几次尝试,并且有不止一个可行的解决方案 - 所有这些都感觉像黑客。我最终使用的那个只是简单地对递归深度进行了人为的限制。一定有更好的解决方案。
  • 这是一个图搜索问题。你有一个循环图。再次,研究广度优先搜索。您还可以查看 Dijkstra 的算法,了解避免循环的经典示例:您必须跟踪您访问过的节点。
  • @JordanDimov 问题不在于人们不阅读您的问题,问题在于您有一个期望(相当标准)来展示您的尝试以及您遇到的问题。选择你的“最简单”的解决方案,并准确地解释你认为不适合它不适合你的目的。您已经说过性能并不重要,因此相对于衡量的简洁性基线实际上是您问题的关键部分。

标签: python recursion functional-programming


【解决方案1】:

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

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 包含要处理的元素。

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

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

【讨论】:

  • 太好了,谢谢。我正在尝试这个的递归变体(例如,传递一个'seen'设置递归)但没有得到任何地方..堆栈技巧很棒。我仍然想知道是否有一种巧妙的递归方式。
猜你喜欢
  • 2013-02-06
  • 1970-01-01
  • 1970-01-01
  • 2012-02-15
  • 2012-08-10
  • 1970-01-01
  • 2011-06-16
  • 2023-03-11
相关资源
最近更新 更多