As a time-pass activity, I decided to implement a Tree(like) structure in python.
I implemented a Node
class (which alone serves the purpose here) like so:<
Here's a solution that covers most of what you're looking for.
Like any tree algorithm, recurse down the children of the tree, and combine results at each node. Here's the trick: display()
returns a rectangle of text, for example:
aaaaaa
aaaaaa
aaaaaa
Most of the rectangle will be whitespace. Returning only rectangles of text makes it easy to combine results. We'll use the following two helper functions, one to measure block widths, and the other to combine blocks horizontally into larger blocks:
def block_width(block):
try:
return block.index('\n')
except ValueError:
return len(block)
def stack_str_blocks(blocks):
"""Takes a list of multiline strings, and stacks them horizontally.
For example, given 'aaa\naaa' and 'bbbb\nbbbb', it returns
'aaa bbbb\naaa bbbb'. As in:
'aaa + 'bbbb = 'aaa bbbb
aaa' bbbb' aaa bbbb'
Each block must be rectangular (all lines are the same length), but blocks
can be different sizes.
"""
builder = []
block_lens = [block_width(bl) for bl in blocks]
split_blocks = [bl.split('\n') for bl in blocks]
for line_list in itertools.izip_longest(*split_blocks, fillvalue=None):
for i, line in enumerate(line_list):
if line is None:
builder.append(' ' * block_lens[i])
else:
builder.append(line)
if i != len(line_list) - 1:
builder.append(' ') # Padding
builder.append('\n')
return ''.join(builder[:-1])
See where this is going? Children return a rectangle that displays themselves and their descendants, and each node will combine these rectangles into a larger rectangle that contains itself. The rest of the code just renders the dashes and pluses:
class Node:
def display(self): # Here
if not self.children:
return self.name
child_strs = [child.display() for child in self.children]
child_widths = [block_width(s) for s in child_strs]
# How wide is this block?
display_width = max(len(self.name),
sum(child_widths) + len(child_widths) - 1)
# Determines midpoints of child blocks
child_midpoints = []
child_end = 0
for width in child_widths:
child_midpoints.append(child_end + (width // 2))
child_end += width + 1
# Builds up the brace, using the child midpoints
brace_builder = []
for i in xrange(display_width):
if i < child_midpoints[0] or i > child_midpoints[-1]:
brace_builder.append(' ')
elif i in child_midpoints:
brace_builder.append('+')
else:
brace_builder.append('-')
brace = ''.join(brace_builder)
name_str = '{:^{}}'.format(self.name, display_width)
below = stack_str_blocks(child_strs)
return name_str + '\n' + brace + '\n' + below
# SNIP (your other methods)
And we're off to the races!
a
+-+-+---------------------------+
b e f g
+ +-+-------------------------+
c h i k
+ + +-+-+-+-------------+-------------+-+------+
d j l m p r s O P Q
+ + +-+-+-+---------+ +-----+
n q t u w x y R S
+ + +-------+-------+ +---+---+
o v z A M T U Z
+-+-+-+-+-+-+ + + +
B D E H I K L N V a
+ + + +-+-+ +
C F J W X Y b
+
G
(Requirements like "placing a ^ below the parent" are left as an exercise for the reader)