Displaying a tree in ASCII

后端 未结 2 1326
萌比男神i
萌比男神i 2021-01-05 06:12

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:<

相关标签:
2条回答
  • 2021-01-05 06:32

    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)

    0 讨论(0)
  • 2021-01-05 06:43

    I'd like to suggest to take a look at ETE module http://ete.cgenomics.org which implements the functionality you describe here and much more.

    At the same time, I'd like to provide this entry Pretty Print output in a sideways tree format in console window where a similar question has been asked before. As you can see in such discussion, the _asciiArt function from ETE provides what, I think, you are looking for.

    I hope this helps,

    0 讨论(0)
提交回复
热议问题