Language agnostic 如何用堆栈模拟递归?

Language agnostic 如何用堆栈模拟递归?,language-agnostic,recursion,Language Agnostic,Recursion,我听说任何递归算法都可以用堆栈来表示。最近,我一直在一个可用调用堆栈非常小的环境中开发程序 我需要做一些深层的递归,所以我想知道如何重新编写任何递归算法来使用显式堆栈 例如,假设我有一个这样的递归函数 function f(n, i) { if n <= i return n if n % i = 0 return f(n / i, i) return f(n, i + 1) } 函数f(n,i){ 如果n您的特定示例是尾部递归的,那么使用适当优化的编译器,它不应该消耗任何堆

我听说任何递归算法都可以用堆栈来表示。最近,我一直在一个可用调用堆栈非常小的环境中开发程序

我需要做一些深层的递归,所以我想知道如何重新编写任何递归算法来使用显式堆栈

例如,假设我有一个这样的递归函数

function f(n, i) {
  if n <= i return n
  if n % i = 0 return f(n / i, i)
  return f(n, i + 1)
}
函数f(n,i){

如果n您的特定示例是尾部递归的,那么使用适当优化的编译器,它不应该消耗任何堆栈深度,因为它相当于一个简单的循环。需要明确的是:此示例根本不需要堆栈。

您可以将代码转换为使用堆栈,如下所示:

stack.push(n)
stack.push(i)
while(stack.notEmpty)
    i = stack.pop()
    n = stack.pop()
    if (n <= i) {
        return n
    } else if (n % i = 0) {
        stack.push(n / i) 
        stack.push(i)
    } else {
        stack.push(n) 
        stack.push(i+1)
    }
}
stack.push(n)
堆栈推送(i)
while(stack.notEmpty)
i=stack.pop()
n=stack.pop()

如果(n如果您了解函数调用如何影响进程堆栈,那么您可以自己了解如何进行

调用函数时,一些数据会写入堆栈,包括参数。函数读取这些参数,对它们执行任何操作,并将结果放在堆栈上。您可以做完全相同的事情。您的示例尤其不需要堆栈,因此如果我将其转换为使用堆栈的堆栈,可能看起来有点愚蠢,因此我不打算这样做o给你一个斐波那契的例子:

fib(n)
    if n < 2 return n
    return fib(n-1) + fib(n-2)

function fib(n, i)
    stack.empty()
    stack.push(<is_arg, n>)
    while (!stack.size() > 2 || stack.top().is_arg)
        <isarg, argn> = stack.pop()
        if (isarg)
            if (argn < 2)
                stack.push(<is_result, argn>)
            else
                stack.push(<is_arg, argn-1>)
                stack.push(<is_arg, argn-2>)
        else
            <isarg_prev, argn_prev> = stack.pop()
            if (isarg_prev)
                stack.push(<is_result, argn>)
                stack.push(<is_arg, argn_prev>)
            else
                stack.push(<is_result, argn+argn_prev>)
     return stack.top().argn

您的示例和斐波那契函数都可以在不使用堆栈的情况下迭代重写

下面是一个需要堆栈的示例:

消除递归:

def ack_iter(m, n):
    stack = []
    push = stack.append
    pop = stack.pop
    RETURN_VALUE, CALL_FUNCTION, NESTED = -1, -2, -3

    push(m) # push function arguments
    push(n)
    push(CALL_FUNCTION) # push address
    while stack: # not empty
        address = pop()
        if address is CALL_FUNCTION:
            n = pop()  # pop function arguments
            m = pop()
            if m == 0: # return n + 1
                push(n+1) # push returned value
                push(RETURN_VALUE)
            elif n == 0: # return ack(m - 1, 1)
                push(m-1)
                push(1)
                push(CALL_FUNCTION)
            else: # begin: return ack(m - 1, ack(m, n - 1))
                push(m-1) # save local value
                push(NESTED) # save address to return
                push(m)
                push(n-1)
                push(CALL_FUNCTION)
        elif address is NESTED: # end: return ack(m - 1, ack(m, n - 1))
            # old (m - 1) is already on the stack
            push(value) # use returned value from the most recent call
            push(CALL_FUNCTION)
        elif address is RETURN_VALUE:
            value = pop() # pop returned value
        else:
            assert 0, (address, stack)
    return value
注:此处无需在堆栈上放置
调用函数
返回值
标签和


这并没有真正帮助我。它确实指出我选择了一个糟糕的示例,但它没有真正演示如何使用堆栈进行递归。无论如何,我使用的语言Javascript的大多数解释器甚至没有尾部调用优化。另外:该示例事实上不是尾部递归,因为它实际上没有返回res调用自身的结果。可以通过添加额外参数将其转换为尾部递归实现,但当前不是。@Daskwuff是的,它确实会将调用的结果返回到自身。你在说什么?该示例已编辑。原始示例返回
f(n*2)+1
f(n+1)-3
@PeterOlson我很确定它可以帮助您认识到有时不需要堆栈。这或多或少就是我将要发布的内容(以及问题措辞错误的观察结果;递归总是在调用堆栈中显式使用堆栈)但我认为值得指出的是,在这种情况下,堆栈永远不会超过两个项目,因此您可以免费获得尾部递归观察。是的,这里不需要堆栈,简单的while循环就足够了,但我仍然使用堆栈,因为OP特别要求使用堆栈。可能的代码副本:if(isarg_prev)stack.push()stack.push()stack.push()应更改为:if(isarg_prev)stack.push()stack.push()@user903597很好
def ack(m, n):
    assert m >= 0 and n >= 0
    if m == 0: return n + 1
    if n == 0: return ack(m - 1, 1)
    return ack(m - 1, ack(m, n - 1))
def ack_iter(m, n):
    stack = []
    push = stack.append
    pop = stack.pop
    RETURN_VALUE, CALL_FUNCTION, NESTED = -1, -2, -3

    push(m) # push function arguments
    push(n)
    push(CALL_FUNCTION) # push address
    while stack: # not empty
        address = pop()
        if address is CALL_FUNCTION:
            n = pop()  # pop function arguments
            m = pop()
            if m == 0: # return n + 1
                push(n+1) # push returned value
                push(RETURN_VALUE)
            elif n == 0: # return ack(m - 1, 1)
                push(m-1)
                push(1)
                push(CALL_FUNCTION)
            else: # begin: return ack(m - 1, ack(m, n - 1))
                push(m-1) # save local value
                push(NESTED) # save address to return
                push(m)
                push(n-1)
                push(CALL_FUNCTION)
        elif address is NESTED: # end: return ack(m - 1, ack(m, n - 1))
            # old (m - 1) is already on the stack
            push(value) # use returned value from the most recent call
            push(CALL_FUNCTION)
        elif address is RETURN_VALUE:
            value = pop() # pop returned value
        else:
            assert 0, (address, stack)
    return value
print(ack(2, 4)) # -> 11
print(ack_iter(2, 4))
assert all(ack(m, n) == ack_iter(m, n) for m in range(4) for n in range(6))
print(ack_iter(3, 4)) # -> 125