需要处理嵌套列表时如何将递归转换为迭代?

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 没有改变。但我不会花时间去弄清楚。

以上是关于需要处理嵌套列表时如何将递归转换为迭代?的主要内容,如果未能解决你的问题,请参考以下文章

将函数从递归转换为迭代

将嵌套的可迭代对象转换为列表

内置函数 iter() 如何将 Python 列表转换为迭代器?

如何将深度嵌套的列表转换为字符串

递归如何转换为非递归

python 展开嵌套列表