[英]How recursive functions work inside a 'for loop'
我在“遞歸函數”中還很陌生。 因此,我正在努力思考為什么使用遞歸函數以及遞歸函數是如何工作的,我認為我對此有很好的理解。
兩天前,我正在嘗試解決最短路徑問題。 我有以下圖(在python中):
graph = {'a': ['b', 'c'],
'b': ['c', 'd'],
'c': ['d'],
'd': ['c'],
'e': ['f'],
'f': ['c']}
我只是想找到一條路徑,而不是最短的路徑,所以這是代碼:
def find_path(graph,start,end,path=[]):
path = path + [start]
#Just a Test
print(path)
if start == end:
return path
if start not in graph:
return None
for node in graph[start]:
if node not in path:
new_path = find_path(graph,node,end,path)
if new_path:
#Just a test
print(path)
return new_path
print(find_path({'a':['b','c'],'b':['c','d'],'c':['d'],'d':['c'],'e':
['f'],'f':['c']},'a','d'))
我的起始頂點為“ a”,結束頂點為“ d”。
在第四行中,我僅打印了“路徑”以查看路徑的去向。
在第17行,我還打印了“路徑”,再次進行測試。 結果如下:
['a']
['a', 'b']
['a', 'b', 'c']
['a', 'b', 'c', 'd']
['a', 'b', 'c']
['a', 'b']
['a']
['a', 'b', 'c', 'd']
結果的前四行是代碼第4行上的'print(path)'結果。 但是,第5、6和7行是代碼第17行的'print(path)'結果。
我的問題是為什么路徑列表每次都減少一個頂點?
我一直在尋找解決方案已有2天了。 我去了論壇,閱讀了有關遞歸的文檔並觀看了視頻。 但是,沒有運氣。
如果有人可以回答,我將非常感激。
這是因為遞歸產生的結果是從“最內層”調用到“最外層”調用。 也就是說,第一行17 print
語句從路徑的節點最多的最深遞歸級別開始。 在該級別返回后,將打印下一個級別“向上”(路徑中少一個節點)。 請注意,您的print
功能來遞歸調用后 find_path
。
您可以將其可視化如下:
find_path(..., path=['a']) # Recursion level 1.
|
| find_path(..., path=['a', 'b']) # Recursion level 2.
| |
| | find_path(..., path=['a', 'b', 'c']) # Recursion level 3.
| | print(path) # Prints ['a', 'b', 'c'].
| | return # Return from level 3.
| |
| print(path) # Prints ['a', 'b'].
| return # Return from level 2.
|
print(path) # Prints ['a'].
return # Return from level 1.
如果你想在要打印的單(子)路徑“增加”命令,那么你可以簡單地將print
功能的遞歸調用之前 find_path
。
它是new_path
變量,用於保存遞歸找到的路徑,而path
僅保存到當前節點的路徑。
順便說一句, if new_path:
尚未輸入前一個if
分支, if new_path:
子句可能會失敗,因為那時new_path
是未定義的。
首先,我將解釋回溯的含義。 我也在這里發布了這個答案。
遞歸意味着從同一函數內調用該函數。 現在發生的是,當函數遇到對自身的調用時。想象一下,當函數在此再次遇到調用時,將打開一個新頁面並將控件從舊頁面轉移到該新頁面上,直至該函數的開始。新頁面,旁邊會打開另一個頁面,這樣,新頁面會在舊頁面旁邊不斷彈出。 請注意,所有局部變量僅在其各自頁面的范圍內。 也就是說,如果您要訪問上一頁中的值,則可以將其傳遞給參數中的函數或將變量設置為全局變量。
返回的唯一方法是使用return語句。 當函數遇到該函數時,控件將從新頁返回到調用該頁所在的同一行的舊頁面,並開始執行該行下方的內容。 這是回溯開始的地方。 為了避免出現諸如在數據填滿后再次饋入數據之類的問題,通常需要在每次調用該函數之后放置一個return語句。
現在在您的代碼中
def find_path(graph,start,end,path=[]):
path = path + [start]
#Just a Test
print(path)
if start == end:
return path
if start not in graph:
return None
for node in graph[start]:
if node not in path:
new_path = find_path(graph,node,end,path) <---- when function returns it will start executing from here again.
if new_path:
#Just a test
print(path)
return new_path
請注意,您的path
變量不是全局變量。 這是當地人。 這意味着您每次調用它都將重置。 為避免這種情況,您需要在函數參數中(最后一個)再次傳遞路徑值。
所以最后,當函數在找到d
之后返回時,它返回到先前的狀態,其中path變量中只有a, b, c
。 那就是你打印出來的。
編輯:-以防萬一有人反對,我對使用頁面進行遞歸的解釋完全是非技術性的,如果您想知道它是如何發生的,那么您將不得不閱讀有關激活記錄及其如何將所有狀態推入堆棧的信息
1) find_path
方法首先調用a
作為設定路徑為開始節點['a']
並調用find_path
與方法b
在第17行打印路徑之前作為起始節點。
2)以b
為起始節點的find_path
方法的調用將路徑設置為['a','b']
並以c
為起始節點調用find_path
方法,然后在第17行打印路徑。
3)以c
作為起始節點的find_path
方法的調用將路徑設置為['a','b','c']
並在第17行打印路徑之前以d
作為起始節點調用find_path
方法。
4)以d
作為起始節點的find_path
方法的調用將路徑設置為['a','b','c','d']
打印在第4行並返回。
5)現在,它在執行find_path
方法的第14行中返回,其中c
為起始節點(如第3點所述將路徑設置為['a','b','c']
),並在第17行(即結果的第5行)並返回。
6)這在執行方法find_path
第14行返回,其中b
為起始節點(如第2點所述將路徑設置為['a','b']
,並在第17行打印路徑(即結果的第6行)並返回。
7)這在執行方法find_path
第14行返回, find_path
以a
作為起始節點(如第1點所述將路徑設置為['a']
),並在第17行(即您的第6行)打印路徑結果)並返回。
您可以把它想象成LIFO(后進先出)
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.