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