需要处理嵌套列表时如何将递归转换为迭代?
Posted
技术标签:
【中文标题】需要处理嵌套列表时如何将递归转换为迭代?【英文标题】:How to convert recursion to iteration when need to process nested lists? 【发布时间】:2021-11-26 22:05:00 【问题描述】:我在 Python get_object_by_id 上有一个函数,通过它的 id 恢复对象,根据对象的类型调用不同的函数:
def get_object_by_id(object_id: int) -> tp.Union[int, float, tuple, list, str, bool]:
"""
Restores object by id.
:param object_id: Object Id.
:return: An object that corresponds to object_id.
"""
info = struct.unpack("LL", ctypes.string_at(object_id, 16))
if info[1] == id(int):
return get_int_by_id(object_id)
elif info[1] == id(float):
return get_float_by_id(object_id)
elif info[1] == id(bool):
return get_bool_by_id(object_id)
elif info[1] == id(str):
return get_str_by_id(object_id)
elif info[1] == id(list):
return get_list_by_id(object_id)
elif info[1] == id(tuple):
return get_tuple_by_id(object_id)
else:
return None
我的函数 get_list_by_id 递归地恢复列表:
def get_list_by_id(object_id: int) -> list:
info = struct.unpack("5L", ctypes.string_at(object_id, 40))
size_of_list = str(info[2]) + 'L'
elements_ides = struct.unpack(size_of_list, ctypes.string_at(info[3], 8 * info[2]))
res_list = []
for i in range(info[2]):
res_list.append(get_object_by_id(elements_ides[i]))
return res_list
如果嵌套列表不是很深,它会很好地工作,但否则会超过最大递归深度。我是 Python 新手,我很难理解,如何在不递归的情况下重写这个函数,又不会让它看起来很可怕。
【问题讨论】:
【参考方案1】:好吧,我没有测试它,所以我不知道它是否真的有效,但希望它说明了这一点:如果系统堆栈无法做到这一点,请制作你自己的堆栈。
def get_list_by_id(object_id: int) -> list:
stack_im_working_on = [] # maybe dqueue is faster?
# records are [info, elements_ides, 2=res_list, 3=i]
def push_new_list(object_id: int) -> list:
info = struct.unpack("5L", ctypes.string_at(object_id, 40))
size_of_list = str(info[2]) + 'L'
elements_ides = struct.unpack(size_of_list, ctypes.string_at(info[3], 8 * info[2]))
res_list = []
nonlocal stack_im_working_on
stack_im_working_on.append([info, elements_ides, res_list, 0])
push_new_list(object_id) # start from working on this
while True: # work from top of the stack
info, elements_ides, res_list, i = stack_im_working_on[-1]
while i < info[2]:
if info[1] == id(list): # The recursive call does not happen
push_new_list(elements_ides[i]) # Now we work on this
break # go to while stack_im_working_on
else:
res_list.append(get_object_by_id(elements_ides[i]))
# res_list is shared with the stack frame, but i is not.
stack_im_working_on[-1][3] = i = i + 1 # save that progress
else: # else for while... run when it gets to the end, until NOT i < info[2](and not on break)
# we finished this stack frame, so return the answer
stack_im_working_on.pop() # removes last element (that we just finished)
if stack_im_working_on:
# return to prev stack frame (not done yet)
stack_im_working_on[-1][2].append(res_list)
else:
# return to caller
return res_list
# Not Reached, as the only pop() checks for empty above
我听说人们不喜欢循环末尾的“else”,但它确实很有用。 (我总是对此发表评论) 另一方面,如果您的堆栈真的不会那么大,并且您可以预测最大大小......只需增加系统堆栈大小限制。 (看起来容易多了) https://***.com/questions/3323001/what-is-the-maximum-recursion-depth-in-python-and-how-to-increase-it
import sys
sys.setrecursionlimit(1500)
【讨论】:
有问题:如果 info[1] == id(list) .. info 没有改变。但我不会花时间去弄清楚。以上是关于需要处理嵌套列表时如何将递归转换为迭代?的主要内容,如果未能解决你的问题,请参考以下文章