为啥在这里使用 `any` 会导致该程序超出递归深度,但使用 `for` 循环不会?

Posted

技术标签:

【中文标题】为啥在这里使用 `any` 会导致该程序超出递归深度,但使用 `for` 循环不会?【英文标题】:Why does using `any` here cause this program to exceed recursion depth, but using a `for` loop doesn't?为什么在这里使用 `any` 会导致该程序超出递归深度,但使用 `for` 循环不会? 【发布时间】:2021-12-16 14:24:40 【问题描述】:

输入

sum_possible(2017, [4, 2, 10]) # -> False

使用any 导致RecursionError: maximum recursion depth exceeded

def sum_possible(amount, numbers, cache = None):
  if cache is None:
    cache = 
  if amount in cache:
    return cache[amount]
  if amount == 0:
    return True
  if amount < 0:
    return False
  cache[amount] = any(sum_possible(amount - number, numbers, cache) for number in numbers)
  return cache[amount]

使用解决方案的for 循环

def sum_possible(amount, numbers, cache = None):
  if cache is None:
    cache = 
  if amount in cache:
    return cache[amount]
  if amount == 0:
    return True
  if amount < 0:
    return False
  
  for number in numbers:
    if sum_possible(amount - number, numbers, cache):
      cache[amount] = True
      return True
  
  cache[amount] = False
  return False

【问题讨论】:

【参考方案1】:

默认递归限制为 1000 次调用。虽然它被称为“递归限制”,但它实际上是所有嵌套函数调用的最大深度——我们只是用递归来描述它,因为没有递归就很难达到极限——你需要数百个不同的函数相互调用.超出递归限制的最常见原因是“无限”递归(例如,未能正确检测基本情况)。

在带有for 循环的版本中,允许1000 个递归级别。连同缓存一起,这对于您的测试用例来说已经足够了。

在使用any() 的版本中,有效递归限制减半,因为每个递归调用都在对any() 的调用中。这还不够。

【讨论】:

我认为通过明确说明所谓的“递归限制”实际上与“递归”没有直接关系,而是更普遍地与 所有功能有关,我认为答案可能会更容易理解调用,而不仅仅是递归函数调用。从理论上讲,只要写很长的any(any(any(any(any(any(any(any(any(any(any(any(any(... 就可以在没有任何递归的情况下超过它 @Stef 我已经更新了答案。我不认为这样的嵌套调用会成为问题,因为每个调用都发生在前一个调用完成之后,而不是在它正在进行时。此外,any() 不返回序列,因此它不能作为 any() 的参数。 “我认为这样的嵌套调用不会有问题,因为每个调用都发生在前一个调用完成之后,而不是在它正在进行时。” 呃。你当然是对的。如果它是一个更简单的函数f,称为f(f(f(...))),那么这不会增加深度,因为函数的参数是在调用函数之前评估的。 OP 遇到此问题的原因是递归调用在生成器表达式内,而不是直接在 any 内。 “评估生成器表达式”除了返回生成器之外没有太大影响。但随后 但是当any 已经开始评估时,对sum_possible(amount - number, numbers, cache) 的调用被评估,此时any 调用生成器的.__next__。所以这个问题是因为生成器而发生的,而不是因为any。如果 OP 使用列表理解而不是生成器表达式,则不会发生这种情况:any([sum_possible(amount - number, numbers, cache) for number in numbers])。虽然那时会有另一个问题:***.com/questions/69773670/… 没错。这就是为什么我们在any() 版本中得到两个级别的调用——any() 自己进行递归调用。

以上是关于为啥在这里使用 `any` 会导致该程序超出递归深度,但使用 `for` 循环不会?的主要内容,如果未能解决你的问题,请参考以下文章

为啥导致 ***Error 的递归方法的调用次数在程序运行之间会有所不同? [复制]

Tkinter,错误最大递归深度超出

为啥使用 BeautifulSoup find_all 方法会导致错误(列表索引超出范围)?

C程序为啥会崩溃?

为啥递归调用会导致不同堆栈深度的 ***?

为啥无限递归会导致段错误