如何在python中从另一个带有“begin”和“end”标志的列表构建嵌套列表?

How to construct a nested list from another list with "begin" and "end" flags in python?

提问人:Idle_92 提问时间:7/7/2023 最后编辑:Idle_92 更新时间:7/7/2023 访问量:75

问:

我正在尝试解析一个文件,该文件具有其包含的数据的嵌套结构。数字嵌套在深度上是任意的,每个嵌套都由字符串分隔,例如“begin”和“end”。我想构造一个列表(或字典等),以保留文件中数据结构的嵌套。

举个简单的例子,从一个看起来像这样的列表:

input = [0, 'begin', 1, 2, 'begin', 3, 4, 'end', 5, 'end', 6]

我想去:

[0, [1, 2, [3, 4], 5], 6]

我尝试创建一个递归函数来执行此操作,但无法获得正确的输出。(我会包括我对解决方案的尝试,但由于递归,我无法判断我是否接近)。这个问题应该如何解决?

编辑以添加我的尝试(我希望这不仅会让事情变得混乱):

input = [0, 'begin', 1, 2, 'begin', 3, 4, 'end', 5, 'end', 6]

def create_nest(items, nest):
    for i, item in enumerate(items):
        if item == 'begin':
            nest.append(create_nest(items[i+1:], []))
        elif item == 'end':
            return nest
        else:
            nest.append(item)

nested_list = []
create_nest(input, nested_list)

这将输出:

[0, [1, 2, [3, 4], 3, 4], 1, 2, [3, 4], 3, 4]
Python 递归 嵌套

评论

0赞 Scott Hunter 7/7/2023
如果您发布尝试过的内容,有人可能会帮助您修复它。
1赞 BoppreH 7/7/2023
代码中的错误是,在调用递归后,您仍在处理下一个项目,因此每个嵌套项目都会重复计算。create_nest
0赞 Idle_92 7/7/2023
谢谢,我已经添加了我的尝试。这可能还有很长的路要走,但任何帮助我朝着正确方向前进的帮助将不胜感激
0赞 Idle_92 7/7/2023
@BoppreH 谢谢,在递归调用之后添加现在给了我,所以我想这是一个改进,但显然我仍然缺少一些东西return nest[0, [1, 2, [3, 4]]]

答:

1赞 mozway 7/7/2023 #1

使用递归函数、迭代器来避免跟踪当前项目,并利用列表的可变性:

def to_nested(inpt, out=None):
    it = iter(inpt)   # use an iterator
    if out is None: # optional: just to avoid using a list as default
        out = []    #
    for item in it:
        if item == 'begin': # if begin, let's start a new level
            out.append([])  # add a new list and pass this recursively
            to_nested(it, out=out[-1])
        elif item == 'end': # if end, let's finish this recursion
            return
        else:               # else, add an item to the current level
            out.append(item)
    return out

to_nested([0, 'begin', 1, 2, 'begin', 3, 4, 'end', 5, 'end', 6])
# [0, [1, 2, [3, 4], 5], 6]

请注意,这假定格式正确(结尾数与开头数一样多)。如果没有,您将不得不跟踪它们。

像我一样使用迭代器修改您的方法

inpt = [0, 'begin', 1, 2, 'begin', 3, 4, 'end', 5, 'end', 6]

def create_nest(items, nest):
    items = iter(items)
    for item in items:
        if item == 'begin':
            nest.append(create_nest(items, []))
        elif item == 'end':
            return nest
        else:
            nest.append(item)
    return nest

nested_list = []
create_nest(inpt, nested_list)
# [0, [1, 2, [3, 4], 5], 6]

评论

0赞 BoppreH 7/7/2023
使用是一个非常聪明的解决方案,可以避免重复计算项目。iter
0赞 mozway 7/7/2023
@BoppreH谢谢,我喜欢迭代器和生成器,它很容易做复杂的事情,而不需要跟踪状态;)
1赞 Idle_92 7/7/2023
iter也很好,因为这个问题的真实版本将使用 ,它已经返回具有这种行为的对象,不是吗?open()
1赞 mozway 7/7/2023
@Idle_92是的,那么就没有必要转换为 iter(尽管它仍然可以工作)
2赞 Scott Hunter 7/7/2023 #2

在不递归的情况下完成:

def collapse(lst):
    save = []
    curr = []
    for x in lst:
        if x == 'begin':
            save.append(curr) # remember what you had so far
            curr = []         # start over
        elif x == 'end':
            old = save.pop()  # get back what you had
            old.append(curr)  # add list you just finished
            curr = old        # pick up where you left off
        else:
            curr.append(x)
    return curr

评论

0赞 Idle_92 7/7/2023
避免递归一直是我的首选方法,因此感谢您提供此解决方案。不过,我会在这里接受另一个答案,因为它提供了我正在尝试的解决方案的工作版本。
1赞 Mulan 7/7/2023 #3

线性基准最好用线性程序处理 -

def parse(tokens):
  r = [[]]
  for t in tokens:
    match t:
      case 'begin':
        r.append([])
      case 'end':
        r[-2].append(r.pop())
      case _:
        r[-1].append(t)
  return r[0]
parse([0, 'begin', 1, 2, 'begin', 3, 4, 'end', 5, 'end', 6])
[0, [1, 2, [3, 4], 5], 6]

您可以而且应该为畸形对提供例外 -begin..end

def parse(tokens):
  r = [[]]
  for t in tokens:
    match t:
      case 'begin':
        r.append([])
      case 'end':
        if len(r) < 2:
          raise RuntimeError("unexpected 'end'")
        r[-2].append(r.pop())
      case _:
        r[-1].append(t)
  if len(r) > 1:
    raise RuntimeError("expected 'end'")
  return r[0]
parse([0, 'end', 'begin', 1, 2, 'begin', 3, 4, 'end', 5, 'end', 6])
# RuntimeError: unexpected 'end'
parse([0, 'begin', 1, 2, 'begin', 3, 4, 'end', 5, 6])
# RuntimeError: expected 'end'

尽管它们可能很优雅,但这里的递归解决方案为格式错误的输入提供了不正确的输出。