[英]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.