繁体   English   中英

Python 递归 function 不返回

[英]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 ,也不关心单独lowerupper 最重要的算术被最小化,仅使用// 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程序控制流是可以接受的。 上面我明确地在partitionyield 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

随着时间的推移,我开始更喜欢这个接口,因为它减少了显式空值和检查它们的需要。 这些新的partitionsearch实现行为相同,并产生相同的 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 中谈论递归。 因为二进制搜索非常有效,所以上面的递归解决方案需要一个巨大的输入来溢出堆栈。 也就是说,如果我们愿意,没有什么能阻止我们使用迭代代替递归来编写partitionsearch -

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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM