依赖关系是开发过程中经常遇到的,例如每个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) 一开始先将字典补充完整,即没有依赖其它的模块也加进去,为的就是通过递归方式,统一操作处理,不然,处理这非规则的情况,也比较麻烦,破坏使用递归的条件。
运行结果:
以上便是整个排序过程,如有高见,请不吝赐教