首先,要注意的語義:使用列表[x, y, z]
爲這你打算通過循環的集合;使用元組(x, y, z)
作爲你想要索引的固定長度集合,這個集合不夠大而不能成爲它自己的類。所以,你應該有
net = {
'Freda': (['Olive', 'John', 'Debra'],
['Starfleet Commander', ' Ninja Hamsters', ' Seahorse Adventures']),
'Ollie': (['Mercedes', 'Freda', 'Bryant'],
['Call of Arms', ' Dwarves and Swords', ' The Movie: The Game']),
'Debra': (['Walter', 'Levi', 'Jennie', 'Robin'],
['Seven Schemers', ' Pirates in Java Island', ' Dwarves and Swords'])
}
其次,做一個遞歸問題時,通過你想先用一些例子來發生的,因爲如果你的機器是什麼步驟。處理Freda
時,您的第一項任務是加載她的連接Olive, John, Debra
。你想用這些做什麼?那麼你要嘗試加載橄欖的連接出現故障,試圖加載約翰的連接出現故障,然後嘗試加載黛布拉的連接,那麼你就必須Walter, Levi, Jennie, Robin.
什麼你想在這個名單呢?把它返還?與其他人的朋友串連起來?關於次級連接沒有任何「遞歸」,至少不像人們通常認爲的那樣。換句話說,它似乎與要定義的函數的名稱相匹配:
def get_secondary_connections(network, person):
primary_connections, movies = network.get(person, ([], []))
out = []
for friend in primary_connections:
secondary_connections, movies = network.get(person, ([], []))
out.extend(secondary_connections)
return out
無需遞歸。我們什麼時候需要遞歸?那麼,如果我們想找到大家誰這個人是由朋友或朋友-的-A-朋友或連接到一個朋友-的-A-朋友-的-A-朋友,然後我們需要探索整個圖和遞歸可能會有所幫助。當然,我們可能也希望這個東西不要包含重複項,所以我們可能需要使用set
而不是list
。
的首種嘗試可能是:
def get_all_connections(network, person):
primary_connections, movies = network.get(person, ([], []))
out = set(primary_connections) # copy this list into an output set
for friend in primary_connections:
out = out.union(get_all_connections(network, person))
return out
而且現在你會發現,的確,在排序網絡的錯,這件事情很容易超過最大遞歸深度。這是一個簡單的網絡:
net = {
'Alice': (['Bob'], []),
'Bob': (['Alice'], [])
}
爲什麼會發生這種情況?因爲找到Alice的所有朋友,你需要找到Bob的所有朋友,但要找到Bob的所有朋友,你需要找到Alice的所有朋友。那麼我們如何克服這一點?
我們可以要求所有鮑勃的朋友不包括那些通過愛麗絲來。這將需要一個完整的清單,否則我們只會絆倒其他循環的情況下,如:
net = {
'Alice': (['Bob'], []),
'Bob': (['Carol', 'Dylan'], []),
'Carol': (['Alice'], []),
'Dylan': (['Bob'], [])
}
注意,當我們問Bob的朋友,我們將遞歸兩個卡羅爾和迪倫,我們需要告訴既他們排除都愛麗絲和鮑勃,因爲已經被處理。因此,導致
def get_all_connections(network, person, excluding=()):
if person in excluding:
return set() # we exclude by immediately returning the empty set.
excluding_us_too = excluding + (person,)
primary_connections, movies = network.get(person, ([], []))
out = set(primary_connections)
for friend in primary_connections:
out = out.union(get_all_connections(network, person, excluding_us_too))
return out
這個週期檢測策略是由幾個不同的名稱,但通常這裏的元組被稱爲「路徑」,因爲當我們處理任何的迪倫的朋友也說('Dylan', 'Bob', 'Alice')
,這意味着我們通過先訪問愛麗絲,然後是鮑勃,然後是迪倫來到迪倫的朋友那裏。
請注意,'卡羅爾'在迪倫的路上無處可尋;有時這可能是一件好事,有時候不會:如果Dylan連接到Carol並且應用到Carol的get_all_connections即使在排除Alice和Bob和Dylan的情況下也會產生一百萬個結果,那麼我們可以預期這兩個結果都會爲Dylan產生相同的結果,然後當我們到達Bob時,我們必須將這兩百萬個結果編成union
,這些結果只有一百萬 - 這是很多不必要的工作!
因此,另一個刺將是保持人員處理隊列和我們處理的人員。在這一點上你不會想要使用遞歸,你更願意使用正常的循環結構。
def get_all_connections(network, person):
visited, to_visit, out = set(), set([person]), set()
while len(to_visit) > 0:
visiting = to_visit.pop()
visited.add(visiting)
connections, movies = network.get(visiting, ([], []))
for friend in connections:
out.add(friend)
if friend not in visited:
to_visit.add(friend)
return out
像這樣的循環可以被轉換成一個高效的遞歸,但不幸的是Python不使這種風格遞歸的效率,使運動的學術,而不是實際的。
沒有最終的條件。您每次發送'network',因此它永遠不會結束 –
您提供的代碼不會顯示您描述的問題。我傾向於猜測,實際代碼中的問題來自關係圖中的一個或多個循環。您需要添加一種方法來忽略已經遍歷的節點。 –