[英]Python recursive function not returning
我正在嘗試編寫一個遞歸 function 來返回排序單詞列表中單詞的 position,或者在找不到單詞時返回None
。 以下是代碼:
def partition(t,kw,upper,lower):
if ((upper-lower) > 1):
pivot = lower + (upper-lower)//2
print("pivot is now {}".format(pivot))
if (kw >= t[pivot]):
lower = pivot
print("searching on the right",upper,lower)
return(partition(t,kw,upper,lower))
elif (kw <= t[pivot-1]):
upper = pivot-1
print("searching on the left",upper,lower)
return(partition(t,kw,upper,lower))
else:
return None
#that means the keyword is between t[pivot-1] and t[pivot]
#which means it doesnt exist in the list
if (upper - lower) <= 1:
if (kw == t[upper]):
print("found element {} at upper bound {}".format(kw,upper))
return(upper)
elif (kw == t[lower]):
print("found element {} at lower bound {}".format(kw,lower))
return(lower)
else:
return None
def search(t, kw):
u = len(t)
partition(t,kw,u,0)
如您所見,每當我調用它時,我都會返回 function(不返回是使用遞歸調用的常見錯誤)。 同時,這是我使用的示例數組:
['', '', '150', '150', '1997,with', '36', '49', 'An', 'Annotated', 'Annotation', 'Bibliography', 'China', 'Chinese', 'Chinese', 'Classical', 'During', 'Dynasty', 'Hong', 'Hong', 'Hong', 'Hong', 'Hong', 'In', 'It', 'Kong', 'Kong', 'Kong', 'Kong,', 'Kong.', 'Mainland', 'Poets', 'Qing', 'They', 'Together,', 'Writings', 'a', 'a', 'a', 'a', 'a', 'active', 'activity,', 'addition', 'almost', 'and', 'and', 'and', 'and', 'and', 'and', 'annotations', 'anthologies', 'basic', 'been', 'before.', 'bibliographic', 'bibliographies,', 'by', 'carry', 'ci-poetry', 'ci-poetry', 'classical', 'collected', 'commentaries', 'compilation,', 'compilations', 'compiled', 'covered,', 'development', 'events', 'focused', 'form', 'form', 'formation,', 'from', 'from', 'has', 'help', 'hidden', 'in', 'in', 'in', 'in', 'includes', 'individual', 'information', 'information', 'introduces', 'invaluable', 'is', 'late', 'literary', 'literati', 'literature', 'literature.', 'membership,', 'most', 'never', 'not', 'of', 'of', 'of', 'of', 'of', 'of', 'of', 'of', 'of', 'of', 'offer', 'on', 'on', 'on', 'on', 'order', 'over', 'past', 'periods', 'pioneer', 'pity', 'poetic', 'poetry', 'poetry', 'poetry', 'poet’s', 'political', 'previous', 'previously', 'published', 'refuge', 'research', 'sequel', 'shi-', 'shi-', 'societies', 'societies', 'societies', 'societies.', 'societies.', 'splendor,', 'that', 'that', 'the', 'the', 'the', 'the', 'the', 'the', 'the', 'the', 'the', 'the', 'the', 'the', 'these', 'these', 'these', 'this', 'times', 'to', 'to', 'to', 'to', 'to', 'to', 'took', 'tools', 'topic', 'tradition.', 'turmoil', 'two', 'uncover', 'understand', 'understanding', 'unique', 'up', 'various', 'very', 'volume,', 'which', 'works,', 'worthwhile', 'would', 'years,']
我在搜索"Qing"
這個詞,它應該在第 31 個 position 中結束。 現在似乎 function 可以找到我需要的單詞,但無法返回:
the result is
pivot is now 92
searching on the left 91 0
pivot is now 45
searching on the left 44 0
pivot is now 22
searching on the right 44 22
pivot is now 33
searching on the left 32 22
pivot is now 27
searching on the right 32 27
pivot is now 29
searching on the right 32 29
pivot is now 30
searching on the right 32 30
pivot is now 31
searching on the right 32 31
found element Qing at lower bound 31
None
我嘗試搜索與遞歸函數相關的問題,但似乎沒有太多相關內容。 SO上的大多數帖子都是由於沒有返回遞歸function引起的,我真的不確定這里出了什么問題。
您的 function 似乎工作正常。 我想你只是忘了從搜索返回,即
def search(t, kw):
u = len(t)
lower = partition(t,kw,u,0)
return lower
輸出
...
searching on the right 32 30
pivot is now 31
searching on the right 32 31
found element Qing at lower bound 31
31
單一用途,簡化
因為它的寫入partition
是一個很大的 function,它有很多參數試圖做很多事情。 是否有可能將它的一些關注點分開並使其更有用? 我們能否抽象出一些復雜性以使 function 更易於編寫且不易出錯?
要查看改進partition
的方法,我們將首先查看 Python 的range
數據結構。 在您的實現中,您使用了以下算術來得出解決方案 -
upper - lower > 1
lower + (upper - lower) // 2
kw <= t[pivot - 1]
upper = pivot - 1
upper - lower <= 1
上面的認知開銷是巨大的,並且出現一次性錯誤的可能性很高。 使用范圍r
,我們可以找到 pivot p
並使用[]
有效切片,同時避開我們所有的頭痛 -
r = range(1,100)
p = len(r) // 2
print("input", r) # input range(1, 100)
print("pivot", p) # pivot 49
print("guess", r[p]) # guess 50
print("left ", r[0:p]) # left range(0, 50)
print("right", r[p+1:]) # right range(51, 100)
這是應用於不同范圍的相同算法 -
r = range(44,66)
p = len(r) // 2
print("input", r) # input range(44, 66)
print("pivot", p) # pivot 11
print("guess", r[p]) # guess 55
print("left ", r[0:p]) # left range(44, 55)
print("right", r[p+1:]) # right range(56, 66)
分割
使用這些屬性,我們可以編寫一個只關注單個范圍的通用partition
-
def partition(r):
if len(r) == 0: return (yield None) # base case
pivot = len(r) // 2 # pivot
(a, b) = yield r[pivot] # guess
if a < b: yield from partition(r[0:pivot]) # recur on left
elif a > b: yield from partition(r[pivot + 1:]) # recur on right
else: return None # equal
上面,我們的 function 不關心單詞列表t
、要搜索的關鍵字kw
,也不關心單獨lower
和upper
。 最重要的算術被最小化,僅使用// 2
來找到pivot
和+1
來計算右側范圍。 范圍錯誤的可能性已大大降低。
搜索
現在我們可以將search
寫成partition
的一種特殊化形式,它接受一個單詞列表t
和一個要搜索的關鍵字kw
-
def search(t, kw):
def loop(p, v): # loop
i = p.send(v) # get guess
if i is None: return None # base case
elif kw == t[i]: return i # found it! return i
else: return loop(p, (kw, t[i])) # recur with comparables
r = range(0, len(t)) # init range
return loop(partition(r), None) # loop over partition of r
這是一個簡單的partition
loop
。 在每次迭代中,我們從分區生成器p
中獲得一個索引i
。 如果沒有索引,我們已經到了搜索的結尾。 否則,如果kw
等於t[i]
,我們找到了答案並可以返回它。 否則kw
不等於t[i]
,所以我們在p
發送(kw, t[i])
重復作為下一個比較。
使用我們的search
function 很簡單 -
r = search(words, "Qing")
if r is None:
print("no result")
else:
print(f"found at index: {r}")
found at index: 31
如果我們搜索一個不存在的詞——
r = search(words, "Wxyz")
if r is None:
print("no result")
else:
print(f"found at index: {r}")
no result
小心輕放
Python 生成器具有獨特的接口,它們是少數例外情況之一,其中使用try..except
程序控制流是可以接受的。 上面我明確地在partition
中yield None
並檢查loop
中的None
但 Python 提供了一個StopIteration
異常,它允許我們消除一些混亂 -
def partition(r):
if len(r) == 0: return # stop
p = len(r) // 2 # pivot
(a, b) = yield r[p] # guess
if a < b: yield from partition(r[0:p]) # recur left
if a > b: yield from partition(r[p+1:]) # recur right
# (implict) return None
現在,當我們編寫search
時,我們可以跳過對None
的空檢查,並繼續假設我們有一個有效的猜測。 否則,如果生成器耗盡,我們會捕獲停止異常並返回None
-
def search(t, kw):
def loop(p, v): # loop
i = p.send(v) # get guess
if kw == t[i]: return i # match?
return loop(p, (kw, t[i])) # recur
try:
r = range(0, len(t))
return loop(partition(r), None)
except StopIteration: # base case
return None
隨着時間的推移,我開始更喜歡這個接口,因為它減少了顯式空值和檢查它們的需要。 這些新的partition
和search
實現行為相同,並產生相同的 output。
解耦副作用
這里要注意的另一個重要事項是partition
function 中不存在print
等副作用。 這使其可用於程序的其他部分,這些部分可能會比較其他數據結構或希望以不同的方式格式化 output -
下面我們可以編寫一個簡單的search
修改,它可以調試 output -
def search(t, kw):
def loop(p, v):
i = p.send(v)
print(f"searching index {i}: {kw} == {t[i]}?") # one change
if kw == t[i]: return i
return loop(p, (kw, t[i]))
try:
r = range(0, len(t))
return loop(partition(r), None)
except StopIteration:
return None
讓我們在調試到位的情況下查看示例的 output -
r = search(words, "Qing")
if r is None:
print("no result")
else:
print(f"found at index: {r}")
searching index 92: Qing == literati?
searching index 46: Qing == and?
searching index 23: Qing == It?
searching index 35: Qing == a?
searching index 29: Qing == Mainland?
searching index 32: Qing == They?
searching index 31: Qing == Qing?
found at index: 31
我們的第二個例子——
r = search(words, "Wxyz")
if r is None:
print("no result")
else:
print(f"found at index: {r}")
searching index 92: Wxyz == literati?
searching index 46: Wxyz == and?
searching index 23: Wxyz == It?
searching index 35: Wxyz == a?
searching index 29: Wxyz == Mainland?
searching index 32: Wxyz == They?
searching index 34: Wxyz == Writings?
no result
沒有遞歸
你會經常聽到人們在 Python 中談論遞歸。 因為二進制搜索非常有效,所以上面的遞歸解決方案需要一個巨大的輸入來溢出堆棧。 也就是說,如果我們願意,沒有什么能阻止我們使用迭代代替遞歸來編寫partition
和search
-
def partition(r):
while len(r):
p = len(r) // 2
(a, b) = yield r[p]
if a < b: r = r[0:p]
if a > b: r = r[p+1:]
if a == b: break
def search(t, kw):
try:
r = range(0, len(t))
p = partition(r)
v = None
while True:
i = p.send(v)
if kw == t[i]: return i
v = (kw, t[i])
except StopIteration:
return None
正如這個很好的答案所指出的,您忽略了在search
function 中添加return
語句。 但是,您的代碼中有一些可以改進的地方:
def partition(t, kw, upper, lower):
if upper - lower > 1:
pivot = lower + (upper - lower) // 2
print("pivot is now {}".format(pivot))
if kw >= t[pivot]:
lower = pivot
print("searching on the right", upper, lower)
return partition(t, kw, upper, lower)
if kw <= t[pivot - 1]:
upper = pivot - 1
print("searching on the left", upper, lower)
return partition(t, kw, upper, lower)
if upper - lower <= 1:
if kw == t[upper]:
print("found element {} at upper bound {}".format(kw, upper))
return upper
if kw == t[lower]:
print("found element {} at lower bound {}".format(kw, lower))
return lower
def search(t, kw):
u = len(t)
return partition(t, kw, u, 0)
lst = ['', '', '150', '150', '1997,with', '36', '49', 'An', 'Annotated', 'Annotation', 'Bibliography', 'China', 'Chinese', 'Chinese', 'Classical', 'During', 'Dynasty', 'Hong', 'Hong', 'Hong', 'Hong', 'Hong', 'In', 'It', 'Kong', 'Kong', 'Kong', 'Kong,', 'Kong.', 'Mainland', 'Poets', 'Qing', 'They', 'Together,', 'Writings', 'a', 'a', 'a', 'a', 'a', 'active', 'activity,', 'addition', 'almost', 'and', 'and', 'and', 'and', 'and', 'and', 'annotations', 'anthologies', 'basic', 'been', 'before.', 'bibliographic', 'bibliographies,', 'by', 'carry', 'ci-poetry', 'ci-poetry', 'classical', 'collected', 'commentaries', 'compilation,', 'compilations', 'compiled', 'covered,', 'development', 'events', 'focused', 'form', 'form', 'formation,', 'from', 'from', 'has', 'help', 'hidden', 'in', 'in', 'in', 'in', 'includes', 'individual', 'information', 'information', 'introduces', 'invaluable', 'is', 'late', 'literary', 'literati', 'literature', 'literature.', 'membership,', 'most', 'never', 'not', 'of', 'of', 'of', 'of', 'of', 'of', 'of', 'of', 'of', 'of', 'offer', 'on', 'on', 'on', 'on', 'order', 'over', 'past', 'periods', 'pioneer', 'pity', 'poetic', 'poetry', 'poetry', 'poetry', 'poet’s', 'political', 'previous', 'previously', 'published', 'refuge', 'research', 'sequel', 'shi-', 'shi-', 'societies', 'societies', 'societies', 'societies.', 'societies.', 'splendor,', 'that', 'that', 'the', 'the', 'the', 'the', 'the', 'the', 'the', 'the', 'the', 'the', 'the', 'the', 'these', 'these', 'these', 'this', 'times', 'to', 'to', 'to', 'to', 'to', 'to', 'took', 'tools', 'topic', 'tradition.', 'turmoil', 'two', 'uncover', 'understand', 'understanding', 'unique', 'up', 'various', 'very', 'volume,', 'which', 'works,', 'worthwhile', 'would', 'years,']
print(search(lst, "Qing"))
Output:
pivot is now 92
searching on the left 91 0
pivot is now 45
searching on the left 44 0
pivot is now 22
searching on the right 44 22
pivot is now 33
searching on the left 32 22
pivot is now 27
searching on the right 32 27
pivot is now 29
searching on the right 32 29
pivot is now 30
searching on the right 32 30
pivot is now 31
searching on the right 32 31
found element Qing at lower bound 31
31
實際上,也可以消除每秒的if
語句(例如,刪除if
行並取消縮進其內容) ,但為了便於閱讀,保留它們也可以。
此外,如果您使用的是 python 3,則可以將"Example number {}".format(5)
元素替換為f"Example number {5}"
。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.