用迭代函数的两个连续“尾”递归调用转换函数

时间:2018-05-15 03:48:42

标签: loops recursion stack iteration

我已经看到很多关于如何将函数转换为迭代版本的单个递归调用的示例;但是,当最后有两个递归调用时呢?

这是我在Python中的意思的一个例子:

def doit(x, y, L):
  if x < y:
    x += 1
  else:
    x -= 1
    y -= 1
  if y > 0:
    L.append(x)
    doit(x, y, L)
    doit(x - 1, y - 1, L)

#usage
L = []
doit(3, 5, L)
print(L)

请注意,两个递归调用都在最后。无论我在这两个递归调用之前在代码中做了什么,是否有一种通用方法将这样的东西转换为迭代版本?请记住,我上面提供的只是一个例子。

1 个答案:

答案 0 :(得分:0)

不,没有通用简单方法,因为有尾递归的情况。即使你最后有一个递归调用但函数不是尾递归(例如return 5+foo(x))也是如此。您必须显式维护一个堆栈,然后它甚至可以用于最后有两个以上递归调用的情况。

要了解原因,请注意尾递归函数中的每个变量(意味着参数和局部变量)在任何给定时刻最多只能有一个可能的值。所以你永远不需要在RAM中维护它们的多个版本。因为下一次递归调用的时刻,一个好的编译器将破坏它们当前的版本并在它们当前所在的同一位置分配新版本。所以它基本上只是修改变量而不是分配新的。

因此对于没有“max 1 version”保证的正常递归函数,在主循环之前声明一个堆栈。其中的每个元素都是所有参数的元组。在循环内部,获取堆栈的最后一个元素(并从堆栈中删除它)。然后只需在循环内复制/粘贴函数体。但是,除了进行下一次递归调用之外,创建一个包含该调用的所有参数值的元素并将其推送到堆栈上。您的代码已修改:

A = [] #your initial 'L'
stack = [(2,4,A)] #push values of initial call
while stack:
    x,y,L = stack.pop()
    print(x,y)
    if x < y:
        x += 1
    else:
        x -= 1
        y -= 1
    if y > 0:
        L.append(x)
        stack.append((x, y, L))
        stack.append((x - 1, y - 1, L))

然而,只有当递归调用的顺序无关紧要时,他才能工作,就像dfs一样。否则,我对“内部堆栈”有一个想法,但这个答案已经太长了。