Python 是不是具有用于一阶递归关系的迭代递归生成器函数?

Posted

技术标签:

【中文标题】Python 是不是具有用于一阶递归关系的迭代递归生成器函数?【英文标题】:Does Python have an iterative recursion generator function for first-order recurrence relations?Python 是否具有用于一阶递归关系的迭代递归生成器函数? 【发布时间】:2016-06-09 23:15:39 【问题描述】:

是否有内置函数或标准库函数大致相当于

def recur_until(start, step_fu, stop_predicate=lambda _: False):
    current = start
    while not stop_predicate(current):
        yield current
        current = step_fu(current)

def recur_while(start, step_fu, predicate=lambda _: True):
    current = start
    while predicate(current):
        yield current
        current = step_fu(current)

甚至只是

def recur(start, step_fu):
    current = start
    while True:
        yield current
        current = step_fu(current)

在任何版本的 Python 中? (后者与itertools.takewhile结合使用时与其他两个一样好。)

像这样的生成器函数将允许迭代地计算某些递归定义的序列,即一阶递归关系。

虽然这些在需要时实施起来并不难,但我觉得它们应该是itertools or maybe functools 的一部分,但如果是,我还无法在文档中找到它。


使用示例:

list(recur_until(2, lambda x: x**2 - 1, lambda x: x > 1e4))
# [2, 3, 8, 63, 3968]

也应该适用于非数字元素:

list(recur_until('', lambda x: '[]()'.format(x, len(x)), lambda x: len(x) > 30))
# ['',
#  '[](0)',
#  '[[](0)](5)',
#  '[[[](0)](5)](10)',
#  '[[[[](0)](5)](10)](16)',
#  '[[[[[](0)](5)](10)](16)](22)']

【问题讨论】:

即等效于 Haskell 的 iterate 函数。 iterate (\x -> x + 1) 0 = 0, 1, 2, 3, 4, 5, ... @chepner iteratetakeWhile 结合使用。 我不知道如何处理“X 存在吗?”这样的问题。我相信答案是“不”。除了链接到您已链接的文档之外,我无法证明该答案的合理性。 @SvenMarnach 是的。我专注于 itertools 中缺少的部分,但您确实需要 iteratetake,dropwhile 的组合。 @DSM,是的,我知道存在问题的问题是否定答案基本上无法证明。但由于我对标准库在这里提供的内容特别感兴趣,所以我不知道如何以不同的方式提出我的问题。 【参考方案1】:

在 Python 3.3+ 中,新的 itertools.accumulate 可以与其他 itertools 结合使用来实现此目的

例如:

>>> from itertools import accumulate, repeat, takewhile
>>> fun = accumulate(range(2, 10), lambda x, _: x**2 - 1)
>>> list(fun)
[2, 3, 8, 63, 3968, 15745023, 247905749270528, 61457260521381894004129398783]
>>> fun = takewhile(lambda y: y < 1e4, accumulate(repeat(2), lambda x, _: x**2 - 1))
>>> list(fun)
[2, 3, 8, 63, 3968]

accumulate 接受一个序列和一个带有 2 个参数的函数:第一个是累加值,第二个是序列中的下一个值。在这种情况下,我们只需要第一个参数,它将是传递给accumulate 的序列的第一个元素,用于第一次调用传递的函数,以及该函数的返回值用于后续调用。

因此,我们只需要将传递序列的开头作为我们的初始值——在本例中为2。序列其余部分的内容无关紧要,但我们可以使用它的长度来控制我们想要多少元素(如第一个示例)或创建一个无限生成器(如第二个示例)。

【讨论】:

嘿,在我看到你的答案之前不久,我刚刚启动了另一台装有 Python 3.4 的计算机来测试类似的东西。【参考方案2】:

缺少的链接是您需要一些东西来将递归步进函数转换为生成器。一旦你有了它,你就可以使用任何 itertools 方法。

def recur_to_gen(step_fu, current, sentinel=None):
    while current != sentinel:
        yield current
        current = step_fu(current)


matches = itertools.takewhile(predicate, recur_to_gen(step_fu, start))

recur_to_gen 建议添加到itertools 可能是合理的。

【讨论】:

通常称为iterate,例如在 Haskell 或 functional Python library. 不幸的是,我认为 Python 中的“迭代”与 iter 密切相关,这更多是关于通过一个序列进行步进,而不是递归地将步进函数应用于一个项目以获得下一个项目.但不管叫什么名字,我认为itertools 会是这样的好地方。 @StefanPochmann 函数recur_to_gen() 是一个生成器函数(因为它是yields)。这里没有生成器表达式。 @SvenMarnach:我认为 SP 引起了人们对 PM 第一句话的注意——OP 的原始函数(虽然称为 recur)是非递归的,并且已经是一个 genfunc。 @DSM - 在它调用自身的意义上不是递归的,但它通过调用当前项目上的步进函数来遍历一个不必要连续的项目链以获得下一个。【参考方案3】:

functional 包提供了模拟这个的部分。

from functional import dropWhile, iterate    
recur = dropWhile(predicate, iterate(step_func, start))

例如,

>>> next(dropWhile(lambda x : x < 10, iterate(lambda x: x + 1, 2)))
10

dropWhileitertools.dropwhile 并没有什么不同。)

【讨论】:

最新的可用版本适用于 Python 2.5。更新的 Python 功能包:github.com/kachayev/fn.py 它仍然有效(通过pip 安装,但我没有对其进行广泛测试)但是是的,我认为functional 是一次性的概念验证,从未真正得到支持.

以上是关于Python 是不是具有用于一阶递归关系的迭代递归生成器函数?的主要内容,如果未能解决你的问题,请参考以下文章

python 3中使用堆栈的递归与迭代

应该选择递归而不是迭代?

python学习笔记4--迭代器生成器装饰器递归

Python之迭代器生成器装饰器和递归

使迭代和递归的代码纯粹是迭代的

Python用迭代(yield)和递归解决八皇后问题