如何处理嵌套列表?

时间:2010-06-06 03:19:36

标签: python parsing list

假设我有一个这样的项目符号列表:

* list item 1
* list item 2 (a parent)
** list item 3 (a child of list item 2)
** list item 4 (a child of list item 2 as well)
*** list item 5 (a child of list item 4 and a grand-child of list item 2)
* list item 6

我想将其解析为嵌套列表或其他一些数据结构,这使得元素之间的父子关系显式化(而不是依赖于它们的内容和相对位置)。例如,这是一个元组列表,其中包含一个项目及其子项列表(等等):

编辑:希望是一个更正确的列表示例,其中列表中的每个元素都是一个元组,其中包含:子弹的文本以及子项列表(如果适用)(以相同的形式)。

<击>

<击>
    [('list item 1',),
     ('list item 2', [('list item 3',), ('list item 4', [('list item 5',)])]
     ('list item 6',)]

<击>

[('list item 1',),
 ('list item 2', [('list item 3',), ('list item 4', [('list item 5',)])]),
 ('list item 6',)]

我试图用纯Python和一些Pyparsing实验来做这件事,但我没有取得进展。我有两个主要问题:

  1. 我需要采用什么策略来完成这项工作?我知道递归是解决方案的一部分,但我很难在这个和斐波那契序列之间建立联系。
  2. 我确定我不是第一个这样做的人,但我不知道问题的术语,可以搜索有关该主题的更多信息。有什么问题与此有关,这样我才能更多地了解解决这类问题?

3 个答案:

答案 0 :(得分:5)

在搜索算法的视图中,您提供的项目符号实际上是由Depth-First-Search生成的序列。所以我的策略是用dfs-sequence重建树结构。

以下是python代码:

from collections import deque
def dfsBullet(bullet,depth):
    """
       parse the subtree with depth and the startnode of bullet[0]
    """
    li = []
    if depth != 0:
            item = bullet.popleft()
            li.append(item.split(' ',1)[1])
    while (len(bullet) != 0):
            item = bullet[0]
            #apply same algo to the child node
            if len(item.split(' ',1)[0]) > depth:
                    sublist = dfsBullet(bullet, len(item.split(' ')[0]))
            #we have traverse all childnode, so go back 
            else:
                    return li
            #add child tree to the list
            li.append(sublist)
    return li

答案 1 :(得分:2)

我无法解析你想要的结果 - 它似乎比相应的闭括号有更多的开括号,我不明白它背后的逻辑。

为了使树结构明确,例如:

data = '''* list item 1
* list item 2
** list item 3
** list item 4
*** list item 5
* list item 6'''.splitlines()

class Node(object):
  def __init__(self, payload):
    self.payload = payload
    self.children = []
  def show(self, indent):
    print ' '*indent, self.payload
    for c in self.children:
      c.show(indent+2)

def makenest(linelist):
  rootnode = Node(None)
  stack = [(rootnode, 0)]
  for line in linelist:
    for i, c in enumerate(line):
      if c != '*': break
    stars, payload = line[:i], line[i:].strip()
    curlev = len(stars)
    curnod = Node(payload)
    while True:
      parent, level = stack[-1]
      if curlev > level: break
      del stack[-1]
    # a child node of the current top-of-stack
    parent.children.append(curnod)
    stack.append((curnod, curlev))
  rootnode.show(0)

makenest(data)

当然,show方法的存在只是为了验证解析字符串和创建树的部分是否正常工作。如果你可以更准确地指定你想要将树转换为嵌套元组和列表的确切方式,我相信很容易将类Node添加到类class Node中 - 那么,请你给出这个缺失的规范......?

编辑:既然OP现在已经澄清了,它确实很容易满足规范。只需将以下方法添加到 def emit(self): if self.children: return (self.payload, [c.emit() for c in self.children]) else: return (self.payload,)

makenest

并将代码的最后三行(makenest的最后一行,空白的一行,以及对 return [c.emit() for c in rootnode.children] print(makenest(data)) 的模块级调用)更改为:

print

[('list item 1',), ('list item 2', [('list item 3',), ('list item 4', [('list item 5',)])]), ('list item 6',)] 之后的括号是冗余的但在Python 2中是无害的,在Python 3中是必需的,所以我把它们放在那里以防万一; - )。

通过这些微小的更改,我的代码按照要求运行,现在发出

{{1}}

答案 2 :(得分:1)

跟踪您正在解析的当前“深度”。

  • 如果下一行的深度大于当前深度,则递归调用具有新深度的解析器,然后将该调用的结果添加到当前列表。
  • 如果下一行的深度等于当前深度,请将其添加到当前列表中。
  • 如果下一行的深度小于当前深度,则返回当前列表。