[英]Python Recursive Example Explanation
所以目前正在線上通過麻省理工學院的OpenCourseWare計算機科學課程,我無法理解其中一個遞歸的例子。
def f(L):
result = []
for e in L:
if type(e) != list:
result.append(e)
else:
return f(e)
return result
當給出以下輸入時:
print f([1, [[2, 'a'], ['a','b']], (3, 4)])
輸出是:
[2, 'a']
我無法理解這個函數是如何工作的,或者它在做什么。 該函數最終不應該將每個字符串或int添加到結果列表中嗎? 我只是需要幫助,試圖了解這個功能如何“結束”和“解除”
我覺得輸出應該是:
[1,2,'a','a','b',3,4]
任何幫助將不勝感激!
函數f
返回它遇到的第一個平面列表的(淺) 副本以及深度優先搜索 。
為什么? 那么首先讓我們來看看基本情況 : 不包含列表清單。 像[1,'a',2,5]
。 在這種情況下, if
語句將始終成功,因此e
所有元素將添加到result
並返回result
。
那么遞歸案例呢? 這意味着是一個列表中的元素。 例如[1,['a',2],5]
。 現在對於第一個元素, if
成功,因此將1
添加到result
列表中。 但是對於第二個元素['a',2]
, if
失敗了 。 這意味着我們使用['a',2]
對f
執行遞歸調用。 現在,由於該列表不包含任何子列表,我們知道它將返回該列表的副本。
但請注意,我們會立即return
該遞歸調用的結果。 因此,從我們采用else
分支的那一刻起, result
就不再重要了:我們將返回f(e)
返回的內容。
如果我們做出假設,我們就無法構造一個無限深的子列表循環(實際上我們可以,但在這種情況下我們將獲得堆棧溢出異常),我們最終將獲得一個平面列表並獲得該副本。
示例 :如果我們采用您的樣本輸入[1, [[2, 'a'], ['a','b']], (3, 4)]
。 我們可以追蹤電話。 所以我們首先在該列表上調用f
,它將生成以下“跟蹤”:
# **trace** of an example function call
f([1, [[2, 'a'], ['a','b']], (3, 4)]):
result = []
# for 1 in L:
# if type(1) == list: # fails
# else
result.append(1) # result is now [1]
# for [[2,'a'],['a','b']] in L:
# if type([[2,'a'],['a','b']]) == list: succeeds
return f([[2,'a'],['a','b']])
result = []
# for [2,'a'] in L:
# if type([2,'a']) == list: succeeds
return f([2,'a'])
result = []
# for 2 in L:
# if type(2) == list: fails
# else:
result.append(2) # result is now [2]
# for 'a' in [2,'a']:
# if type('a') == list: fails
# else:
result.append('a') # result is now [2,'a']
return [2,'a']
return [2,'a']
return [2,'a']
展平 :
如果您想要壓縮列表而不是返回第一個平面列表,則可以將代碼重寫為:
def f(L):
result = []
for e in L:
if type(e) != list:
result.append(e)
else:
result += f(e)
return result
請注意,這只會壓縮list
(甚至不是list
的子類)。
所以根據你建議的答案,我看到你了解代碼的概念。 它越陷越深,直到找到一個元素。 但是看看上層的后退步驟:
當它第一次到達最深點時([2,'a']列表的元素),它在這個級別上完成循環並返回結果2和a。 這是一個RETURN語句...這意味着循環是停止的,因此沒有找到其他元素。
現在懸而未決的問題是,為什么它沒有顯示1作為結果的一部分? 出於同樣的原因,RETURN是較低級別(2,a)和較高級別結果的結果。 如果將“result”更改為全局變量,結果將為[1,2,'a']
最好的祝福
當運行到第一個自底向下的list-element(不包含列表)時,作為發布的函數返回/退出 - 這可以防止遍歷遞歸的所有其他分支。 例如:
print( f([1, [[2, 'a', [3, 'b', [4, 'c']]], ['a','b']], (3, 4)]) )
# gives: [4, 'c']
print( f([1, ['X','Y'], [[2, 'a', [3, 'b', [4, 'c']]], ['a','b']], (3, 4)]) )
# gives: ['X','Y']
導致此行為的關鍵點是該行
result = []
這會將列表重置,並將每次調用函數的結果重置為空列表。 這樣,只有一個項目從遞歸調用鏈中返回。
順便說一句,下面的函數f做了你所期望的,不是嗎?
def f(L, result):
for e in L:
if type(e) != list:
result.append(e)
else:
f(e, result)
result=[]; f([1, [[2, 'a', [3, 'b', [4, 'c']]], ['a','b']], (3, 4)], result) print( result )
# gives: [1, 2, 'a', 3, 'b', 4, 'c', 'a', 'b', (3, 4)]
result=[]; f( [1, ['X','Y'], [[2, 'a', [3, 'b', [4, 'c']]], ['a','b']], (3, 4)], result); print( result )
# gives: [1, 'X', 'Y', 2, 'a', 3, 'b', 4, 'c', 'a', 'b', (3, 4)]
注意:(3,4)是TUPLE而不是列表......
如果這些項目本身不是列表,則上述函數f從列表中收集項目。 在特殊情況下,當列表中的項目是列表時,該函數會調用自身來從此列表中收集項目。 通過這種方式,無論人們需要挖掘多深,每一個元素都會被收集到層次結構中。 這是遞歸的美妙 - 一個函數調用自己做了訪問所有分支和他們的葉子在樹下的“魔力”:)
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.