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

运行结果:

以上便是整个排序过程,如有高见,请不吝赐教