依存関係のあるものを順に並べる。

やりたいこと

A -B -> C
  -D -> E

というような依存関係がある時、依存関係の解決したものから順に利用したい。上の例だと例えばこんな感じ。

C , B , E , D , A

DAG -> トポロジカルソート -> 反転と同じような感じ。

たぶん、探せば絶対あるけど、とりあえず必要なので作ってみる。

  • node.py
  • node_serializer.py

node.pyは依存関係の木を作るためのもの。
node_serializerはそれを上のような順序で取り出すためのもの。

node.py
# -*- coding: utf-8 -*-
import sys

class Node(object):
    def __init__(self, k, v=None):
        self.k = k
        self.v = v
        self._children = {}

    @property
    def children(self):
        return self._children.values()

    def add(self, child):
        self._children[child.k] = child

    def __getattr__(self, k):
        return self._children.get(k)

    def __str__(self):
        return "%s: %s" % (self.k, self.v)

    def inspect(self, indent=0):
        sys.stdout.write(" "*indent)
        sys.stdout.write(str(self))
        sys.stdout.write("\n")

        indent_ = indent + 2
        for child in self._children.values():
            child.inspect(indent_)
        
if __name__ == "__main__":
    x = Node("x", "x")
    xx = Node("xx", "y")
    x.add(xx)
    xy = Node("xy", "z")
    x.add(xy)
    xxx = Node("xxx", "α")
    x.xx.add(xxx)
    x.inspect()

実行例

$ python node.py 
x: x
  xx: y
    xxx: α
  xy: z
node_serializer.py
# -*- coding:utf-8 -*-
from node import Node
import copy

class NodeSerializer(object):
    def __init__(self, keyfn= lambda x : x):
        self.keyfn = keyfn
        self.candidates = []
        self.consumed = set()

    def add(self, node):
        self.candidates.append(node)
        return self

    def _children_are_consumed(self, node):
        for child in node.children:
            if not self.keyfn(child) in self.consumed:
                return False
            if not self._children_are_consumed(child):
                return False
        return True
        

    def _reset(self):
        self.consumed = set()

    def __iter__(self): #手抜き
        self._reset()

        xs = copy.copy(self.candidates)

        while xs:
            x = xs.pop(0)

            if self._children_are_consumed(x):
                yield x
                self.consumed.add(self.keyfn(x))
            else:
                xs.append(x)

if __name__ ==  "__main__":
    a = Node("a")
    b = Node("b")
    c = Node("c")
    a.add(b)

    ns = NodeSerializer(lambda n : n.k).add(a).add(b).add(c)
    for e in ns:
        print e

実行結果

python node_serializer.py
# [a -> b, c] という依存関係
b: None
c: None
a: None

もうちょっと増やした。

a = Node("a")
b = Node("b")
c = Node("c")
d = Node("d")
e = Node("e")
f = Node("f")
g = Node("g")
h = Node("h")
i = Node("i")
j = Node("j")
a.add(b)
a.add(c)
a.add(g)
d.add(e)
d.add(g)
c.add(f)
e.add(f)
h.add(i)
i.add(j)

xs = [a, b, c, d, e, f, g, h, i, j]
ns = reduce(lambda ns, x: ns.add(x), xs, NodeSerializer())

print [i.k for i in ns]

 > ['b', 'f', 'g', 'j', 'c', 'e', 'i', 'a', 'd', 'h']