这更像是一个谜题而不是问题本身,因为我认为我已经有了正确的答案 - 我只是不知道如何测试它(如果它在所有情况下都有效)。
我的程序从用户那里获取有关需要加载哪些模块的输入(以未排序的列表或集合的形式)。其中一些模块依赖于其他模块。模块依赖信息的存储方式如下:
all_modules = { 'moduleE':[], 'moduleD':['moduleC'], 'moduleC':['moduleB'], 'moduleB':[], 'moduleA':['moduleD'] }
其中moduleE没有依赖关系,而moduleD依赖于moduleC等......
此外,没有所有可能模块的明确列表,因为用户可以生成自己的模块,并且我不时创建新模块,因此这个解决方案必须相当通用(因此测试适用于所有情况)。
我想要做的是获取要按顺序运行的模块列表,以便依赖于其他模块的模块仅在其依赖项之后运行。
所以我编写了以下代码来尝试这样做:
def sort_dependencies(modules_to_sort,all_modules,recursions):
## Takes a small set of modules the user wants to run (as a list) and
## the full dependency tree (as a dict) and returns a list of all the
## modules/dependencies needed to be run, in the order to be run in.
## Cycles poorly detected as recursions going above 10.
## If that happens, this function returns False.
if recursions == 10:
return False
result = []
for module in modules_to_sort:
if module not in result:
result.append(module)
dependencies = all_modules[module]
for dependency in dependencies:
if dependency not in result:
result.append(dependency)
else:
result += [ result.pop(result.index(dependency)) ]
subdependencies = sort_dependencies(dependencies, all_modules, recursions+1)
if subdependencies == False:
return False
else:
for subdependency in subdependencies:
if subdependency not in result:
result.append(subdependency)
else:
result += [ result.pop(result.index(subdependency)) ]
return result
它的工作原理如下:
>>> all_modules = { 'moduleE':[], 'moduleD':['moduleC'], 'moduleC':['moduleB'], 'moduleB':[], 'moduleA':['moduleD'] }
>>> sort_dependencies(['moduleA'],all_modules,0)
['moduleA', 'moduleD', 'moduleC', 'moduleB']
请注意,不会返回'moduleE',因为用户不需要运行它。
问题是 - 它是否适用于任何给定的all_modules字典和任何给定的required_to_load列表?是否有我可以放入的依赖图和许多用户模块列表来尝试,如果它们有效,我可以说所有图形/用户列表都可以工作吗?
在Marshall Farrier的出色建议之后,看起来我正在尝试做的是拓扑排序,所以在看了this和this之后我实现了以下内容:
编辑:现在进行循环依赖检查!
def sort_dependencies(all_modules):
post_order = []
tree_edges = {}
for fromNode,toNodes in all_modules.items():
if fromNode not in tree_edges:
tree_edges[fromNode] = 'root'
for toNode in toNodes:
if toNode not in tree_edges:
post_order += get_posts(fromNode,toNode,tree_edges)
post_order.append(fromNode)
return post_order
def get_posts(fromNode,toNode,tree_edges):
post_order = []
tree_edges[toNode] = fromNode
for dependancy in all_modules[toNode]:
if dependancy not in tree_edges:
post_order += get_posts(toNode,dependancy,tree_edges)
else:
parent = tree_edges[toNode]
while parent != 'root':
if parent == dependancy: print 'cyclic dependancy found!'; exit()
parent = tree_edges[parent]
return post_order + [toNode]
sort_dependencies(all_modules)
但是,像上面那样的拓扑排序对整个树进行排序,并且实际上并不返回用户需要运行的模块。当然,具有树的拓扑类型有助于解决这个问题,但它与OP的问题并不是同一个问题。我认为对于我的数据,拓扑排序可能是最好的,但是对于像apt / yum / pip / npm中的所有包一样的大图,在OP中使用原始算法可能更好(我不知道它是否是实际上适用于所有情况......)因为它只排序需要使用的东西。
所以我提出的问题没有答案,因为问题实际上是“我如何测试这个?”