-1

I am trying to do a recursive function in Python to get all parents starting from a given child - eg. if I want to find all parents starting from A - A has parents B and C, B has parents D and E, C has parents F and G, so the function should return set: {B,C,D,E,F,G} I have a class GenTree, where I have all people saved in self.people (instances, not names) and a class Person, which has method get_parents() to either return False or a tuple of parents (2 instances of class Person)

Here is the whole file with the classes and methods:

class Person:
    def __init__(self, name, gender, education, father = False, mother = False):
        self.name = name
        self.gender = gender
        self.education = education
        self.father = father
        self.mother = mother
        self.children = []
    def add_parent(self, inst):
        if inst.gender=="m":
            self.father = inst
        else:
            self.mother = inst
    def add_child(self, inst):
        self.children.append(inst)
    def has_parent(self):
        return True if self.father or self.mother else False
    def get_parents(self):
        if self.has_parent():
            if self.father and self.mother: return self.father, self.mother
            if self.father and not self.mother: return self.father
            if self.mother and not self.father: return self.mother
        else:
            return ()

class GenTree:
    def __init__(self):
        self.people = {}
    def load_from_file(self, file_name):
        data = open(file_name, "r")
        people = {}
        reading = "person"
        for line in data:
            line = line.rstrip()
            reading = "fam" if line=="" else reading
            if reading=="person":
                thisInfo = line.split(";")
                thisName = thisInfo[0]
                thisGender = thisInfo[1]
                thisEd = thisInfo[2]
                self.people[thisName] = Person(thisName, thisGender, thisEd)
            else:
                if line == "":
                    continue
                thisInfo = line.split("=>")
                for i in range(len(thisInfo)):
                    thisInfo[i] = thisInfo[i].rstrip()
                    thisInfo[i] = thisInfo[i].strip(" ")
                self.people[thisInfo[1]].add_parent(self.people[thisInfo[0]])
                self.people[thisInfo[0]].add_child(self.people[thisInfo[1]])

    def get_all_parents(self, child_name):
        child = self.people[child_name]
        parents = child.get_parents()
        if parents:
            for parent in parents:
                return parents + self.get_all_parents(parent.name)
        return parents

g = GenTree()
g.load_from_file("data_a")
print([i.name for i in g.get_all_parents('Katka')])

and here is the file with given data:

Ales;m;c
Alexandr;m;c
Anna;f;h
Dana;f;e
Daniela;f;u
David;m;u
Hana;f;h
Jana;f;u
Jarda;m;c
Jindra;m;u
Jirka;m;u
Jitka;f;h
Juraj;m;u
Karel;m;e
Katka;f;c
Lenka;f;h
Leon;m;h
Leona;f;c
Leos;m;e
Lida;f;e
Ludmila;f;h
Magdalena;f;c
Matej;m;u
Michaela;f;h
Michal;m;e
Patricia;f;h
Petr;m;h
Richard;m;e
Sasa;f;u
Stefan;m;h
Tereza;f;h
Tomas;m;e
Vaclav;m;e
Vojtech;m;c
Zdena;f;e
Zdenek;m;h

Ales        => Zdenek
Tereza      => Zdenek
Alexandr    => Vojtech
Zdena       => Vojtech
David       => Anna
Sasa        => Anna
Jarda       => Daniela
Patricia    => Daniela
Vojtech     => Jindra
Daniela     => Jindra
Zdenek      => Jirka
Anna        => Jirka
Vaclav      => Juraj
Michaela    => Juraj
Ales        => Ludmila
Tereza      => Ludmila
Ludmila     => Magdalena
Juraj       => Magdalena
Juraj       => Tomas
Anna        => Tomas
Zdenek      => Lida
Sasa        => Lida
Lida        => Leona
Zdenek      => Leona
Tomas       => Stefan
Anna        => Stefan
Tomas       => Karel
Leona       => Karel
Tomas       => Leos
Leona       => Leos
Tomas       => Lenka
Leona       => Lenka
Juraj       => Matej
Lenka       => Matej
Juraj       => Leon
Anna        => Leon
Juraj       => Richard
Lenka       => Richard
Richard     => Petr
Ludmila     => Petr
Petr        => Michal
Dana        => Michal
Stefan      => Dana
Anna        => Dana
Michal      => Hana
Lida        => Hana
Michal      => Jana
Lida        => Jana
Petr        => Jitka
Dana        => Jitka
Jirka       => Katka
Dana        => Katka

Now, "print([i.name for i in g.get_all_parents('Katka')])" should return:

{'Jirka', 'Dana', 'Zdenek', 'Anna', 'Stefan', 'Ales', 'Tereza', 'David', 'Sasa', 'Tomas', 'Juraj', 'Vaclav', 'Michaela'}

but it returns

['Jirka', 'Dana', 'Zdenek', 'Anna', 'Ales', 'Tereza']

But it always does "self.get_all_parents(parent.name)" ONLY for the first one in the for loop Also, I don't know how to return it as a set (I tried to do get_all_parents(self, child_name, allparents=set()), but it doesn't seem to reset the allparents set with every call of function)

  • 3
    Your example is not executable and therefore not verifiable. Post as much code that is needed to execute your function (but not more). – timgeb Dec 10 '17 at 15:35
  • Please post a sample of what your tree looks like or the code for `GenTree`. – Ajax1234 Dec 10 '17 at 15:41
  • 1
    I couldn't make it really brief, but I tried to get rid of anything useless right now. I edited the post with the codes – Ludovit Kopcsanyi Dec 10 '17 at 15:50
  • ignoring other things your using return inside a loop . a function ends whenever it hit the return so your for loop is completely ignored – Naqib Hakimi Dec 10 '17 at 15:52

1 Answers1

0

using for loop with recursive is not a good idea in most cause . actually they are kind of the same thing . Haskell (functional programming language) do not have for loop

def get_all_parents(self, child_name):
        child = self.people[child_name]
        parents = child.get_parents()
        if parents:
            return parents + self.get_all_parents(parent[0].name) + self.get_all_parents(parent[1].name)

        return parents

a simple recursive call means calling itself one or more times

return parents + self.get_all_parents(parent[0].name) + self.get_all_parents(parent[1].name)

here get_all_parents will return it's value exactly in it's place + ....

edit

you can use the for loop here in special cases .but this kind codding can be very confusing and hard to reason

def get_all_parents(self, child_name , ret = ()):
    child = self.people[child_name]
    parents = child.get_parents()
    if parents:
        for p in parents:
            ret = self.get_all_parents(p.name , ret)
    return parents + ret
                

in this case we need an extra object to travel between every call and add the result to it. its something like shared object between all function calls. you can easy turn a list to set this question has already been answered take a look How to construct a set out of list items in python?

Community
  • 1
  • 1
Naqib Hakimi
  • 874
  • 4
  • 15
  • I tried that one before, it works, but how can I return it as a set? – Ludovit Kopcsanyi Dec 10 '17 at 23:26
  • this is another question and if you wan to turn list to set it will remove all duplicate names . are you sure you wanna do this ? – Naqib Hakimi Dec 11 '17 at 17:08
  • Yes, that's the point, to return set without duplicate names:) – Ludovit Kopcsanyi Dec 12 '17 at 06:56
  • Also, how would I do something like that for children, if everybody has different number of children? I can't do return children + self.get_all_children(children[0]) + self.get_all_children(children[1]) etc, because someone has 1 child, someone has 3 children. Only solution I can think of is a for loop, but how? – Ludovit Kopcsanyi Dec 12 '17 at 08:48