提问人:Idle_92 提问时间:7/7/2023 最后编辑:Idle_92 更新时间:7/7/2023 访问量:75
如何在python中从另一个带有“begin”和“end”标志的列表构建嵌套列表?
How to construct a nested list from another list with "begin" and "end" flags in python?
问:
我正在尝试解析一个文件,该文件具有其包含的数据的嵌套结构。数字嵌套在深度上是任意的,每个嵌套都由字符串分隔,例如“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]
答:
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'
尽管它们可能很优雅,但这里的递归解决方案为格式错误的输入提供了不正确的输出。
评论
create_nest
return nest
[0, [1, 2, [3, 4]]]