总是可以将递归转换为尾递归吗?
我很难将以下Python函数转换为尾递归函数。
def BreakWords(glob):
"""Break a string of characters, glob, into a list of words.
Args:
glob: A string of characters to be broken into words if possible.
Returns:
List of words if glob can be broken down. List can be empty if glob is ''.
None if no such break is possible.
"""
# Base case.
if len(glob) == 0:
return []
# Find a partition.
for i in xrange(1, len(glob) + 1):
left = glob[:i]
if IsWord(left):
right = glob[i:]
remaining_words = BreakWords(right)
if remaining_words is not None:
return [left] + remaining_words
return None
答案 0 :(得分:2)
我不确定是否总是如此,但大多数递归函数都可以实现为尾递归。除了Tail Recursion不同于Tail Recursion优化。
常规递归函数中的返回值由两种类型的值组成:
让我们看一个例子:
def factorial(n):
if n == 1 return 1
return n * factorial(n-1)
帧f(5)“存储”它自己的计算结果(5)和f(4)的值。如果我调用factorial(5),就在堆栈调用开始崩溃之前,我有:
[Stack_f(5): return 5 * [Stack_f(4): 4 * [Stack_f(3): 3 * ... [1[1]]
请注意,除了我提到的值之外,每个堆栈还存储函数的整个范围。因此,递归函数f的内存使用是O(x),其中x是我必须进行的递归调用的数量。所以,如果我需要1kb的RAM来计算阶乘(1)或阶乘(2),我需要~100k来计算阶乘(100),依此类推。
在Tail Recursion中,我使用参数将每个递归帧中的部分计算结果传递给下一个递归帧。让我们看看我们的阶乘示例,Tail Recursive:
def factorial(n):
def tail_helper(n, acc):
if n == 1 or n == 2: return acc
return tail_helper(n-1, acc + n)
return tail_helper(n,0)
让我们看看它在factorial(4)中的帧:
[Stack f(4, 5): Stack f(3, 20): [Stack f(2,60): [Stack f(1, 120): 120]]]]
看到差异? 在“常规”递归调用中,返回函数以递归方式组成最终值。在Tail Recursion中,他们只引用基本案例(评估的最后一个)。我们将累加器称为跟踪旧值的参数。
常规递归函数如下:
def regular(n)
base_case
computation
return (result of computation) combined with (regular(n towards base case))
要在Tail递归中转换它,我们:
查找
def tail(n):
def helper(n, accumulator):
if n == base case:
return accumulator
computation
accumulator = computation combined with accumulator
return helper(n towards base case, accumulator)
helper(n, base case)
我做了类似的事情:
def BreakWords(glob):
def helper(word, glob, acc_1, acc_2):
if len(word) == 0 and len(glob) == 0:
if not acc_1:
return None
return acc
if len(word) == 0:
word = glob.pop[0]
acc_2 = 0
if IsWord(word.substring[:acc_2]):
acc_1.append(word[:acc_2])
return helper(word[acc_2 + 1:], glob, acc_1, acc_2 + 1)
return helper(word[acc_2 + 1:], glob, acc_1, acc_2 + 1)
return helper("", glob, [], 0)
为了消除你所做的for语句,我用2个累加器做了递归辅助函数。一个存储结果,一个存储我正在尝试的位置。
由于没有状态存储在Tail Call堆栈的非边界案例中,因此它们并不那么重要。然后,一些语言/解释器将旧堆栈替换为新堆栈。因此,如果没有堆栈帧限制呼叫数量,尾部呼叫的行为就像一个for循环。
但遗憾的是,Python不是其中之一。当堆栈大于1000时,你会得到一个RunTimeError。Mr. Guido 认为由于尾部调用优化而导致调试目的的清晰度(由抛出的帧引起)比该功能更重要。真是太遗憾了。 Python有很多很酷的功能,尾递归很棒:/