微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

如果递归函数返回到开头,它如何操作?

如何解决如果递归函数返回到开头,它如何操作?

我是一个新手程序员

我正在查找使用递归函数的问题。虽然我能理解要点,但有一个不清楚的问题,我在调试过程中无法立即破译。感谢您对我的问题的帮助。

问题的概念(合并排序)非常简单,但我对递归函数的一般工作方式感到困惑。 Bellow 是我正在处理的程序(来自佐治亚理工学院 Python 课程):

def mergesort(lst):

    if len(lst) <= 1:
        return lst

    else:

        midpoint = len(lst) // 2
        left = mergesort(lst[:midpoint])

        right = mergesort(lst[midpoint:])

        newlist = []
        while len(left) and len(right) > 0:
            if left[0] < right[0]:
                newlist.append(left[0])
            else:
                newlist.append(right[0])
                del right[0]

        newlist.extend(left)
        newlist.extend(right)

        return newlist

print(mergesort([2,5,3,8,6,9,1,4,7]))

问题:当程序到达这一行 left = mergesort( lst[:midpoint]) 时会发生什么?

根据我的理解,它返回到程序的第一行,然后再次返回到同一行(就像 for 一样)。

所以它一直在返回!!!但是,这使我无法阅读该程序。一般来说,程序如何处理递归函数是我的主要问题。我无法理解它的工作方式。

解决方法

当程序到达这一行 left = mergesort(lst[:midpoint]) 时会发生什么?根据我的理解,它返回到程序的第一行并再次下降到同一行...

每次程序重复执行时,它都会使用一个较小的列表调用 mergesort。我们称之为“子问题”-

def mergesort(lst):
    if len(lst) <= 1:
        # ...
    else:
        midpoint = len(lst) // 2           # find midpoint
        left = mergesort(lst[:midpoint])   # solve sub-problem one
        right = mergesort(lst[midpoint:])  # solve sub-problem two
        # ...

例如,如果我们首先使用 4 元素列表调用 mergesort -

mergesort([5,2,4,7])

输入列表 lst 不符合基本情况,因此我们转到 else 分支 -

def mergesort(lst):                       # lst = [5,7]
    if len(lst) <= 1:
        # ...
    else:
        midpoint = len(lst) // 2          # midpoint = 2
        left = mergesort(lst[:midpoint])  # left = mergesort([5,2])
        right = mergesort(lst[midpoint:]) # right = mergesort([4,7])
        # ...

注意 mergesort 是通过 [5,2][4,7] 子问题调用的。让我们对第一个子问题重复这些步骤 -

left = mergesort([5,2])
def mergesort(lst):                       # lst = [5,2]
    if len(lst) <= 1:
        # ...
    else:
        midpoint = len(lst) // 2          # midpoint = 1
        left = mergesort(lst[:midpoint])  # left = mergesort([5])
        right = mergesort(lst[midpoint:]) # right = mergesort([2])
        # ...

所以它一直在返回!!!

不完全是。当我们在这一步解决子问题时,事情看起来就不一样了。当输入为一个元素或更少时,满足基本情况,函数退出-

left = mergesort([5])
def mergesort(lst):     # lst = [5]
    if len(lst) <= 1:   # base case condition satisfied
        return lst      # return [5]
    else:
        ...             # no more recursion

left 子问题的递归停止并返回 [5] 的答案。这同样适用于 right 子问题 -

right = mergesort([2])
def mergesort(lst):     # lst = [2]
    if len(lst) <= 1:   # base case condition satisfied
        return lst      # return [2]
    else:
        ...             # no more recursion

接下来我们返回我们的第一个 left 子问题 -

left = mergesort([5,2]
    if len(lst) <= 1:
        # ...
    else:
        midpoint = len(lst) // 2          # midpoint = 1
        left = mergesort(lst[:midpoint])  # left = [5]        <-
        right = mergesort(lst[midpoint:]) # right = [2]       <-
        # ...
        return newlist                    # newlist = [2,5]

您现在将针对第一个 right 子问题重复这些步骤 -

right = mergesort([4,7])
def mergesort(lst):                       # lst = [4,7]
    if len(lst) <= 1:
        # ...
    else:
        midpoint = len(lst) // 2          # midpoint = 1
        left = mergesort(lst[:midpoint])  # left = mergesort([4])
        right = mergesort(lst[midpoint:]) # right = mergesort([7])
        # ...

再次,递归停止,因为新的 leftright 子问题是满足基本情况的单元素列表 -

right = mergesort([4,7]
    if len(lst) <= 1:
        # ...
    else:
        midpoint = len(lst) // 2          # midpoint = 1
        left = mergesort(lst[:midpoint])  # left = [4]       <-
        right = mergesort(lst[midpoint:]) # right = [7]      <-
        # ...
        return newlist                    # newlist = [4,7]

最后最外层的 mergesort 调用可以返回 -

mergesort([5,7])
def mergesort(lst):                       # lst = [5,7]
    if len(lst) <= 1:
        # ...
    else:
        midpoint = len(lst) // 2          # midpoint = 2
        left = mergesort(lst[:midpoint])  # left = [2,5]
        right = mergesort(lst[midpoint:]) # right = [4,7]
        # ...
        return newlist                    # newlist = [2,5,7]
# => [2,7]

综上所述,递归是一种函数式遗产,因此将其与函数式风格结合使用会产生最佳效果。这意味着避免诸如突变、变量重新分配和其他副作用之类的事情。考虑这个替代方案,它通过清楚地分离程序的关注点来降低概念开销 -

def mergesort(lst):
  def split(lst):
    m = len(lst) // 2
    return (lst[:m],lst[m:])

  def merge(l,r):
    if not l:
      return r
    elif not r:
      return l
    elif l[0] < r[0]:
      return [l[0]] + merge(l[1:],r)
    else:
      return [r[0]] + merge(l,r[1:])

  if len(lst) <= 1:
    return lst
  else:
    (left,right) = split(lst)
    return merge(mergesort(left),mergesort(right))

mergesort([5,7])
# => [2,7]
,

您的问题的答案是:copies

每个函数都是一个用于计算的配方

当一个函数被调用时,配方的副本被创建。每次调用都涉及创建一个单独的副本。这就是每个人都可以独立运作的方式,而且他们都不会混在一起。

总的来说,递归函数调用没有什么特别之处。函数调用就是函数调用,不管调用的是什么函数。一个函数被调用,执行它所做的事情,并将其结果返回给调用者。至于递归,你不应该跟踪它。它自己完成工作。你应该向自己证明基本情况是正确的,递归情况是正确的。就是这样。

那么它就可以保证以任何复杂的方式工作,而重点是让我们不关心它究竟是如何做到的,即它的确切步骤顺序。


所以特别在你的情况下,假设 mergesort确实工作正常(等等,什么?没关系,暂停你的一时难以置信),

        left = mergesort(lst[:midpoint])

使用 mergesort 的前半部分调用函数 lst,从它的开始到中点,并存储结果 - 这是 排序的 前半部分,根据假设,-在变量left中;然后

        right = mergesort(lst[midpoint:])

使用mergesort第二一半调用函数lst,从它的中点到它的结尾,并存储结果 - 这是排序 em> 后半部分,假设,-在变量 right;

然后你需要说服自己,其余的代码从这两个排序的半部分创建 newlist 使得 newlist also sorted 以正确的顺序。

然后通过数学归纳法证明mergesort的正确性。

假设它有效,我们证明它确实有效!问题在哪里?没有问题,因为假设的两个案例是针对两个较小的输入(这就是我们的递归案例)。

当我们一遍又一遍地将一个东西分成两部分时,最终我们会得到一个单独的东西,或者一个空的东西。这两个是自然排序的(这是我们的基本情况)。

递归是一种信仰的飞跃。假设这个东西在工作,然后你就可以使用它了。如果您正确使用它,您将因此构建您最初使用的东西!

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。