views:

1077

answers:

2

Data: a dependency list, already verified to be acyclic. So here, 'a' depends on 'b','c','d' (c depends on d), etc...

A = { 'a' :  dict(b=1, c=1),
    'c' : dict(d=1),
    'd' : dict(e=1,f=1,g=1),
    'h' : dict(j=1)
    }

I'd like to have a top-down, recursive solution to let's say, find the chain starting at 'a': a, c, d, e, g, f, b

So, right now (a non-generator solution):

def get_all(D,k):
    L = []
    def get2(D,k):
        L.append(k)
        for ii in D.get(k,[]):
            get2(D, ii)
    get2(D,k)
    return L

Obviously, this is pretty weak :) I've been banging my head about how to how to get yields inside there, and I'd appreciate any py-foo y'all can bring to this.

+3  A: 

Try this:

#!/usr/bin/env python

def get_all(D, k):
    yield k
    for ii in D.get(k, []):
        for jj in get_all(D, ii):
            yield jj

A = { 'a' : dict(b=1, c=1),
    'c' : dict(d=1),
    'd' : dict(e=1,f=1,g=1),
    'h' : dict(j=1)
    }

for ii in get_all(A,'a'):
    print ii

Gives me

steve@rei:~/code/tmp
$ python recur.py
a
c
d
e
g
f
b
freespace
Thanks! I clearly didn't have enough coffee this morning.
Gregg Lind
No problems :-) Maybe it is the case that I have had too much coffee today :P
freespace
Did someone say coffee!? :) Good answer, freespace.
Kevin Little
I believe this would not work in cases where multiple nodes point to the same node. {'a': dict(b=1), 'c': dict(b=1)}
Sridhar Ratnakumar
How would it not work? With the data you have given, my algorithm yields b for the chain starting at a, which appears correct to me.
freespace
+3  A: 

Both answers give the same result, but if my reading of the question is correct give the wrong answer to a simple alteration to the given graph - if you add a dependency on 'c' from 'b' (which doesn't introduce a cycle as the graph is directed) the output is: a c d e g f b d e g f

which isn't totally helpful. Try this small variation, which keeps track of which nodes of the graph have already been visited:

def get_all(D, k, seen=None):
    if not seen:
        seen = set( )
    if k not in seen:
        seen.add(k)
        yield k
        for ii in D.get(k, []):
            for jj in get_all(D, ii, seen):
                yield jj
HenryR
So, the problem with this method is that the storage length again can balloon up to N, where N is the number of nodes in the graph. It is a very useful variation though. For this particular application I don't mind revisiting chains.
Gregg Lind