Python中的递归合并排序和堆栈框架

Python中的递归合并排序和堆栈框架,python,recursion,merge,mergesort,Python,Recursion,Merge,Mergesort,下面的代码(不是我的,只是研究一下)在递归原始列表(即,list_uu)和合并例程之间(正确地)跳转。堆栈帧的流程(即,它们返回的方式和原因尚不清楚,即使在Python教程中观看时也是如此,这是我在下面叙述的内容)。描述代码如何返回以及程序中的问题 def merge(left, right): if not len(left) or not len(right): return left or right result = [] i, j = 0, 0

下面的代码(不是我的,只是研究一下)在递归原始列表(即,
list_uu
)和合并例程之间(正确地)跳转。堆栈帧的流程(即,它们返回的方式和原因尚不清楚,即使在Python教程中观看时也是如此,这是我在下面叙述的内容)。描述代码如何返回以及程序中的问题

def merge(left, right):
    if not len(left) or not len(right):
        return left or right

    result = []
    i, j = 0, 0
    while (len(result) < len(left) + len(right)):
        if left[i] < right[j]:
            result.append(left[i])
            i+= 1
        else:
            result.append(right[j])
            j+= 1
        if i == len(left) or j == len(right):
            result.extend(left[i:] or right[j:])
            break 

    return result

def mergesort(list_):
    if len(list_) < 2:
        return list_

    middle = len(list_)//2
    left = mergesort(list_[:middle])
    right = mergesort(list_[middle:])

    return merge(left, right)


list_ = [7,5,2,1,3]

print(mergesort(list_))
def合并(左、右):
如果不是len(左)或非len(右):
向左或向右返回
结果=[]
i、 j=0,0
而(len(result)
我们遇到的第一个递归调用是
left
(我们希望对列表的左半部分进行排序,然后对左半部分的左半部分进行排序,以此类推,直到我们得到一个大小为1的列表并遇到基本情况)。这个很好用。我们从[7,5,2,1,3]到[7,5]再到[7],并击中基本情况。到目前为止,一切顺利我希望堆栈帧开始返回,但实际情况并非如此。我想它会返回7,但是接下来我们跳转到下一组递归调用,
right
5会重新出现。5出现在堆栈帧中,紧跟在7之前,因此返回的顺序是相反的(这很好)。新参数拆分了5并创建了一个列表,因此设置了基本情况并返回了5


这就是奇怪的地方:程序进入合并步骤,这是正确的它如何“知道”在
右侧
左侧
跳过进一步的递归(我给了它一个完整的列表,其中大部分未被触及)并跳转到合并?此外,它如何知道在没有明确指示的情况下从merge返回mergesort函数,并确切地知道从何处提取?
有人能解释一下吗?传统的算法文本和大多数视频没有任何帮助,因为它们不处理堆栈帧

我认为您的困惑与不理解每个堆栈帧都有自己的执行点有关。每次调用函数时,帧的执行都会暂停,并为函数调用创建一个新帧。当它返回时,前一帧将在其停止的位置拾取。没有“知道”代码下一步应该去哪里的中心位置,每个级别自己处理

在您描述的示例场景中,对
[7,5]
mergesort
调用首先将列表拆分为
[7]
[5]
,然后依次在每个列表上递归,以获得
。当第二个递归调用返回时,它将继续执行代码的下一部分,即
merge
调用,因为这是代码中的下一部分

你可以很清楚地看到逻辑,就在这里:

left = mergesort(list_[:middle])
right = mergesort(list_[middle:])

return merge(left, right)

这告诉您,在这个运行中(以及每个没有达到基本情况的运行),它将递归两次,然后
merge

,这肯定是它的一部分。此外,我认为我开始这样想,return语句中的最终性比实际的更多(即,return不结束fxn,而是返回一个值)。它来自于听觉,“一旦你从你的函数中返回了一些东西,就这样了!之后就不会有其他东西了。”并且把它带得有点太远了。另外,我不知道EIP在其中扮演什么角色,这可能会有所帮助,但我喜欢你的技术性较低的方法。我认为你引用的语句没有错,只是在处理对同一函数的多个递归调用时有点混乱。
return
会结束当前函数的执行,但不会结束其调用方的执行(即使该调用方是进行递归调用的同一个函数)。来电者只是从暂停的地方继续。因此,如果您有六个堆栈帧深,并且您点击了一个
return
语句,那么第六个帧就完成了,您将返回到第五个堆栈帧并继续运行其代码。