【问题标题】:Displaying a tree in ASCII以 ASCII 显示树
【发布时间】:2013-03-18 12:03:59
【问题描述】:

作为一个时间传递活动,我决定在 python 中实现一个Tree(like) 结构。
我实现了一个Node 类(仅用于此处),如下所示:

class Node:
    def __init__(self, name, parent, *data):
        self.name = name
        self.parent = parent
        self.data = data
        self.children = []
        self.is_root = False

    def __repr__(self):
        return 'Node '+repr(self.name)

    def dic(self):
        retval = {self:[]}
        for i in self.children:
            retval[self].append(i.dic())
        return retval

    def display(self): # Here
        pass

    def has_children(self):
        return bool(self.children)

    def get_parent(self):
        return self.parent

    def add_child(self, name, *data):
        child = Node(name, self,*data)
        self.children.append(child)
        return child

如您所见,display 函数未实现。
这是一个示例树。

A = Node('A',Node)
A.is_root = True
B = A.add_child('B')
D = B.add_child('D')
C = A.add_child('C')
E = C.add_child('E')
F = C.add_child('F')
G = C.add_child('G')

这是display 的一些示例输出。

>>> A.display()
    A
  +-^-+
  B   C
  | +-+-+
  D E F G
>>> C.display()
   C
 +-+-+
 E F G

简而言之,
如何从 Node 类“构建”一个 ASCII 树(如上)??

以更长的形式,
打印的“逻辑”是:

  1. 当只有一个孩子时,| 放在孩子的上方。 (D)
  2. 否则,每个孩子的上方都有一个+,(B,C,E,F)
  3. 甚至没有的时候。的孩子,^ 放在父母的下方。 (一)
  4. 否则,(有奇数个子级)+ 放在父级下方。 (C)

我一直在考虑从下面开始。 我意识到必须给每个孩子打电话,但一直无法实现任何(那种或其他的)提供任何接近它的东西。

【问题讨论】:

  • 如果这是一个练习,你应该自己尝试一下,你会学得更好
  • "Drawing presentable trees" by Bill Mill 是我几周前遇到类似问题时使用的。它来自一个基本算法,并增加了对结果必须遵守的某些属性的限制,增加了几个步骤的复杂性。这是一篇很棒的文章,示例非常“通用”。看看吧。
  • @jamylak 这是一个自我给予的“锻炼”,所以,我不认为问这个问题会妨碍我的技能或学习。而且,我也有很多失败的尝试。另外,请阅读第一行...
  • @Schoolboy 文章基本解决了将(x, y)坐标分配给树的节点的问题。然后,您需要解决一个新的(希望是)更简单的问题以获得最终输出:给定一组节点及其坐标,创建一个表示它们的 ASCII 字符串。我要做的第一件事是为xy 选择一个单位大小,找到如何绘制节点和弧线的约定,并手动检查结果如何。然后你必须自动化绘图。
  • 你也可以使用github.com/mbr/asciitree的asciitree包,或者至少看看他是如何实现的。

标签: python tree


【解决方案1】:

这里的解决方案涵盖了您正在寻找的大部分内容。

像任何树算法一样,向下递归树的子节点,并在每个节点处组合结果。诀窍如下:display() 返回一个矩形文本,例如:

aaaaaa
aaaaaa
aaaaaa

大部分矩形将是空白。只返回文本的矩形可以很容易地组合结果。我们将使用以下两个辅助函数,一个用于测量块的宽度,另一个用于将块水平组合成更大的块:

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])

看看这是怎么回事?子节点返回一个显示自己及其后代的矩形,每个节点会将这些矩形组合成一个包含自身的更大矩形。其余代码只呈现破折号和加号:

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)

我们要去参加比赛了!

                             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                          

(像“在父级下方放置一个 ^”这样的要求留给读者作为练习)

【讨论】:

  • 哇,非常感谢,我没想到这么长时间后会有答案.. 太好了.. 非常感谢,非常好的逻辑!值得更多投票!
【解决方案2】:

我建议看一下 ETE 模块 http://ete.cgenomics.org,它实现了您在此处描述的功能等等。

同时,我想提供此条目Pretty Print output in a sideways tree format in console window,以前曾问过类似问题。正如您在此类讨论中看到的那样,ETE 的 _asciiArt 函数提供了我认为您正在寻找的东西。

希望对你有帮助,

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2014-06-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-10-26
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多