[英]How to Convert Recursion to Tail Recursion
是否總是有可能將遞歸轉換為尾遞歸?
我很難將以下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
我不確定是否總是這樣,但是大多數遞歸函數可以實現為尾遞歸。 此外,尾遞歸與尾遞歸優化不同。
常規遞歸函數中的返回值由兩種類型的值組成:
讓我們來看一個例子:
def factorial(n):
if n == 1 return 1
return n * factorial(n-1)
例如,幀f(5)“存儲”它自己的計算結果(5)和f(4)的值。 如果我調用階乘(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 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)
讓我們看一下階乘(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))
要在尾部遞歸中對其進行轉換,我們:
看:
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個累加器執行了遞歸輔助函數。 一種用於存儲結果,一種用於存儲我當前正在嘗試的位置。
由於沒有狀態存儲在尾部調用堆棧的非邊界情況下,因此它們並不是那么重要。 然后,某些語言/解釋器將舊堆棧替換為新堆棧。 因此,在沒有堆棧幀限制調用次數的情況下, 尾部調用的行為就像一個for循環 。
但是不幸的是,Python並不是其中一種情況。 當堆棧大於1000時,您將收到RunTimeError。Guido 先生認為,由於尾部調用優化(由拋出的幀引起)而導致調試目的的清晰度比該功能更為重要。 真可惜 Python有很多很棒的功能性東西,並且在它上面尾部遞歸將是很棒的:/
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.