依赖关系是开发过程中经常遇到的,例如每个JAVA工程都可以依赖很多其它JAVA工程的制品,在整个进行构建时,就需要考虑这种依赖关系了, 不然总是构建不成功的,本文就以此情景为例,实现一个基于这种依赖关系的多个模块排序,以便能正常地整个构建成功。
假设有A,B,C,D,E,F六个模块,它们的依赖关系如下:
A <-- B,C
B <-- D,E
C <-- E
D <-- F
要表达这种抽象的关系,我们可以借助于拓扑图,如下图所示:
这种表达方式就比较清晰了,从中可以得出结论,出度为0(即没有指向其它节点的箭头)的节点排在最前面,排序的过程就是依次将出度为0的节点移出来,所形成的结果即是最终排序了。
这里用Python来表达排序过程的话,需要用字典类型来记录依赖关系,具体代码如下:
1 #!python
2 # coding:GB18030
3 ###################################################################################################
4 #
5 # 基于依赖关系的排序
6 # 情景:A <-- B,C
7 # B <-- D,E
8 # C <-- E
9 # D <-- F
10 #
11 #
12 ####################################################################################################
13
14 import os
15
16 def clearKeyFromVal(key, vals):
17 """从各Value中清除指定的Key
18 相当于在拓扑图中移出一个节点后,要清除所有与该节点的关联,即箭头
19 """
20 for val in vals:
21 if type(val) is tuple and key in val:
22 vl = list(val) #因为要对元组内容修改,需要转为list类型
23 ln = vl.index(key)
24 vl.pop(ln)
25 tn = vals.index(val)
26 if len(vl) == 0:
27 vals[tn] = ""
28 else:
29 vals[tn] = tuple(vl) #将内容改变后,重新转回tuple类型
30 return vals
31
32
33
34 def clearNullVal(keys, vals, result_list):
35 """将值为''的键值对移出字典(值为""的键即为在拓扑图中出度为0的节点)
36
37 递归调用
38 """
39 if "" in vals:
40 nn = vals.index("")
41 vals.pop(nn)
42 key = keys.pop(nn)
43 result_list.append(key) #移出的键,放到结果List中
44 vals = clearKeyFromVal(key, vals) #清除与移出键相关联的箭头,即需要在各个value中也清除该值
45 return clearNullVal(keys, vals, result_list) #注意这里要return结果
46 elif len(vals) == 0:
47 return result_list #移出所有Value后,退出
48 else:
49 print '存在循环依赖'
50 return
51
52
53 def sortModules(dic):
54
55 """根据依赖关系,将多个模块进行排序,被依赖的模块要在前"""
56 keys = dic.keys()
57 vals = dic.values()
58 mlist = []
59 for val in vals:
60 if type(val) is tuple:
61 for tv in val:
62 if tv not in dic:
63 dic[tv] = "" #将没有依赖的键也添加到字典中,以方便后续对整个字典递归处理,逐个将值为""的键移出,形成排序结果
64
65 print dic
66 return clearNullVal(dic.keys(), dic.values(), mlist)
67
68
69 if __name__ == "__main__":
70
71 #通过字典结构来表示依赖关系
72
73 dic={'A':('B','C'),'B':('D','E'),'C':('E', ), 'D': ('F', )}
74 r_list = sortModules(dic)
75 print r_list
注:1)要注意判断是否存在循环依赖,即在拓扑图中形成环,即始终找不到出度为0的节点。
2)移出节点时,运用了递归调用方式。
3)虽然依赖关系是用字典类型来表达的,但处理过程中,将字典的键和值分为两个列表来处理,同步移出节点
4)依赖关系,在字典中是用元组作为Value的(因为它是不可变的,即可哈希的),在操作过程中,又适时与列表类型转换
5) 一开始先将字典补充完整,即没有依赖其它的模块也加进去,为的就是通过递归方式,统一操作处理,不然,处理这非规则的情况,也比较麻烦,破坏使用递归的条件。
运行结果:
以上便是整个排序过程,如有高见,请不吝赐教