[英]Why does this function print a Linked List backwards?
我正在研究Downey的《如何像計算機科學家一樣思考》,並且對他的Linked List的 print_backward()函數有疑問。
首先,這是Downey在Python中的鏈表的實現:
class Node:
#initialize with cargo (stores the value of the node)
#and the link. These are set to None initially.
def __init__(self, cargo = None, next = None):
self.cargo = cargo
self.next = next
def __str__(self):
return str(self.cargo)
我們為此類提供以下貨物和鏈接值:
#cargo
node1 = Node('a')
node2 = Node('b')
node3 = Node('c')
#link them
node1.next = node2
node2.next = node3
要打印鏈接列表,我們使用Downey的另一個功能。
def printList(node):
while node:
print node,
node = node.next
>>>printList(node1)
>>>a b c
一切都很簡單。 但是我不明白以下函數中的遞歸調用如何允許人們向后打印鏈表。
def print_backward(list):
if list == None : return
print_backward(list.next)
print list,
>>>print_backward(node1)
>>>c b a
不會將“ list.next”用作print_backward的值只是給您“ bc”嗎?
注意:以下幾位人士指出,此功能的設計不正確,因為給定任何列表,我們都無法表明它總是可以到達基本情況。 唐尼還在同一章的后面指出了這個問題。
def print_backward(list):
if list == None : return
print_backward(list.next)
print list,
不會將“ list.next”用作print_backward的值只是給您“ bc”嗎?
沒有; 想象一下當a-> b-> c傳遞給print_backward時會發生什么:
“ [bc]”傳遞給print_backward,然后打印“ a”。 但是在打印“ a”之前,“ print_backward”會自行調用。 所以:
在前向打印版本中,它將在執行遞歸調用之前打印每個節點。 在向后打印版本中,它將在執行遞歸調用后打印每個節點。
這不是偶然的。
這兩個函數都會遞歸直到到達列表的末尾。 區別在於在此過程中還是之后進行打印。
函數調用使用堆棧,這是一種后進先出的數據結構,可以記住進行函數調用時計算機在哪里執行代碼。 以一順序放在堆棧中的內容以相反的順序出現。 因此,遞歸以原始調用的相反順序“解開”。 打印在展開過程中進行,即在每個遞歸調用完成之后進行。
如果list不是None,它將調用print_backward,然后打印列表的第一個成員。 擴展,這自然是會發生的事情。 您會看到當呼叫開始返回時,先打印“ c”,然后打印“ b”,再打印“ a”。
實際打印列表時,它會打印第一個節點
print_backward(list='a','b','c')
print_backward(list='b','c')
print_backward(list='c')
print_backward(list=None)
list is None, so return
print 'c'
print 'b','c'
print 'a','b','c'
有時,我發現將遞歸視為僅按一定順序構造要調用的列表就容易了。 隨着函數的繼續,它將建立一堆調用,直到最終到達基本情況為止。 基本情況是不需要進一步分解程序的情況; 在此函數中,基本情況是沒有任何可打印的內容,在這種情況下,我們無需執行return
就可以離開。
當我們展開函數調用的遞歸堆棧時,很酷的事情通常發生在返回的路上。 當前,已在列表的每個元素上調用print_backward
,它現在將“展開”,首先完成最近的調用,最后完成較早的調用。 這意味着,當您在最后一個元素上調用print_backward
創建的“實例”是第一個要完成的元素,因此最后一個元素是要打印的第一個元素,其后是倒數第二個,倒數第三個等等。 ,直到原始功能最終退出。
看一下發生了什么的這種表示形式:
print_backward(node1) #first call to print_backward
print_backward(node2) #calls itself on next node
print_backward(node3) #calls itself on next node
print_backward(None) #calls itself on None. We can now start unwinding as this is the base case:
print Node3 #now the third invocation finishes...
print Node2 #and the second...
print Node1 #and the first.
雖然在較早的元素上首先調用該函數,但實際打印該元素的部分位於遞歸調用之后,因此,在遞歸調用完成之前,它實際上不會執行。 在這種情況下,這意味着print list
部分要等到所有后來的元素都先被打印(以相反的順序)后才能執行,從而使您可以向后打印列表元素。 :D
它正在使用遞歸。 它一直向下“壓縮”直到結束,然后在每次調用返回時打印每個元素。 由於第一個要打印的是最近一次調用的,因此它將向后打印列表。
否。有兩種遞歸:
null
,然后向后處理列表)。 每個函數調用都被壓入堆棧,以供以后處理。 在您的示例中,該函數堆疊為'a'->'b'->'c'->null
,然后在彈出堆疊時,作者通過向后打印來表明:`if null return:print'c' ->打印'b'->打印'a' 在您的情況下,作者僅演示了遞歸的不同概念,並使用該概念將列表向后打印。
您的節點如下所示:
node1 node2 node3
'a' => 'b' => 'c' => None
在第一次調用print_backward
,變量list
的值為'a'
,隨后對print_backward
調用進一步向下移動了一行。 請注意,他們沒有打印任何東西,直到你打中后衛( None
),此時,事情就從后到前的印刷print_backward
收到節點'c'
必須在返回之前print_backward
收到節點'b'
可以打印(因為print語句在函數調用之后)等等。
雖然我認識到這是別人的代碼,但是這里有一些不好的做法-最好在您學習時而不是以后告訴您。 首先,不要使用list
作為變量名,因為它是python中內置函數/類型的名稱。 其次, if obj == None
更好地完成if obj == None
的相等性測試,最后,讓您的類繼承自object
( class node(object):
if obj is None
一個好主意,因為這使它成為新樣式的類。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.