parsing tree from excel sheet

Peter Otten __peter__ at web.de
Sat Jan 31 10:07:55 CET 2015


alb wrote:

> > 
> > def read_tree(rows, levelnames):
> >    root = Node("#ROOT", "#ROOT")
> >    old_level = 0
> >    stack = [root]
> >    for i, row in enumerate(rows, 1):
> 
> I'm not quite sure I understand what is the stack for. As of now is a 
> list whose only element is root.

The stack is used to emulate the function call stack in a recursive 
solution. Every nested call produces a new set of local variables and the 
innermost call corresponds to the top of the stack or the end of the `stack` 
list in my code.

Given a tree

A
  A1
    A11
    A12
  A2

or in another notation

0 A
1 A1
2 A11
2 A12
1 A2

we start with a stack (with the node's children in parens)

[A()]

then look at A1 and see that the level is one deeper than that of A and 
append it to A, the current TOS (top of stack)

[A(A1)]

Next is A11 which is two levels deeper than A, so we take the last child of 
A and put it on the stack, 

[A(A1), A1()]

then add A11 as a child to the new TOS

[A(A1), A1(A11)]

Next is A12 which is one level deeper than A1, so we add it to the current 
TOS

[A(A1), A1(A11, A12)]

Next is A2 which is one level higher than A1, so we keep removing nodes from 
the stack until the current TOS is one level higher than A2. Here we only 
have to remove A1 from the stack to get

[A(A1)]

and after adding A2 to the TOS

[A(A1, A2)]

I have ommitted the children of the children so far, but the actual 
structure is now

[A(A1(A11(), A12()), A2())]

Therefore I just need to return A which contains the complete layout of the 
tree.
 
> >        new_level = column_index(row)
> >        node = Node(row[new_level], levelnames[new_level])
> 
> here you are getting the node based on the current row, with its level.
> 
> >        if new_level == old_level:
> >            stack[-1].append(node)
> 
> I'm not sure I understand here. Why the end of the list and not the 
> beginning?
> 
> >        elif new_level > old_level:
> >            if new_level - old_level != 1:
> >                raise ValueError
> 
> here you avoid having a node which is distant more than one level from 
> its parent.
> 
> >            stack.append(stack[-1].children[-1])
> 
> here I get a crash: IndexError: list index out of range!
> 
> >            stack[-1].append(node)
> >            old_level = new_level
> >        else:
> >            while new_level < old_level:
> >                stack.pop(-1)
> >                old_level -= 1
> >            stack[-1].append(node)
> 
> Why do I need to pop something from the stack??? Here you are saying 
> that if current row has a depth (new_level) that is smaller than the 
> previous one (old_level) I decrement by one the old_level (even if I may 
> have a bigger jump) and pop something from the stack...???
> 
> >    return root
> 
> once filled, the tree is returned. I thought the tree would have been 
> the stack, but instead is root...nice surprise.


> Why do I need to pop something from the stack???

That got me thinking, and I found that my code is indeed much more complex 
than necessary. Sorry for that ;)

As a compensation here is the simplified non-popping version of read_tree():

def read_tree(rows, levelnames):
    root = Node("#ROOT", "#ROOT")
    parents = [root] + [None] * len(levelnames)

    for row in rows:
        level = column_index(row)
        node = Node(row[level], levelnames[level])

        parents[level].append(node)
        parents[level+1] = node

    return root






More information about the Python-list mailing list