任意复杂度的递归与迭代一起复制?

时间:2014-11-07 22:28:57

标签: python loops recursion

递归可以是解决问题的优雅方法。一些数学函数是递归定义的。然而,作为战略问题,Python的仁慈的生活独裁者Guido van Rossum有decided not to provide for tail recursion elimination。尾递归消除会使代码路径从递归函数调用深处追溯到主进程级别。

这意味着通常使用新参数调用自身的函数在执行此操作的次数上受到限制。对于小的递归问题,限制通常是任意大的,但对于可以作为长列表处理的操作,该限制任意小,并且通常默认设置为1000。

这个简单的案例如下所示:

def foo(): 
    foo()

和:

>>> foo()

Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 1, in foo

[... 1000 (default limit on recursion for Python)个相同的行,每个行代表一个执行框架。]

  File "<stdin>", line 1, in foo
RuntimeError: maximum recursion depth exceeded

将递归限制设置得更高的问题是,它可能适用于逐渐增大的问题,但最终你的所有帧都会耗尽内存,导致进程崩溃。

特定于递归的复杂性有两个维度:不同的参数和不同的函数。 所以这里有一些任意复杂度的递归代码,一个可重复性最小的例子,三个函数伪随机选择并传入两个参数,最后一个参数将根据调用函数而变化:

import random

MAX = 10000

def foo(my_list, item):
    my_list.append(item)
    if len(my_list) < MAX:
        return random.choice(FUNCS)(my_list, 'foo')
    else:
        return my_list

def bar(my_list, item):
    my_list.append(item[::-1]) # reverse the string, for variety.
    return random.choice(FUNCS)(my_list, 'bar')

def baz(my_list, item):
    my_list.append(item)
    return random.choice(FUNCS)(my_list, 'baz')

FUNCS = (foo, bar, baz)

# And now call `foo`:

''.join(foo([], 'init!'))[:50]

我们得到追溯:

  File "<stdin>", line 3, in bar
  File "<stdin>", line 3, in bar
  File "<stdin>", line 3, in baz
  File "/usr/lib/python2.7/random.py", line 275, in choice
    return seq[int(self.random() * len(seq))]  # raises IndexError if seq is empty
RuntimeError: maximum recursion depth exceeded while calling a Python object

但是对于较低的递归深度,这是有效的。

MAX = 900
''.join(foo([], 'init!'))[:50]

返回:

'init!foobazoofrabbaroofbarfoooofrabrabbaroofrabrab'

Python初学者会遇到这个问题,最近我被问到这个问题,因为他们不知道如何解决这个问题。所以问题是,可以用Python中的迭代代替任意复杂度的递归吗?还有一个模型可以将递归代码转换成迭代代码吗?

1 个答案:

答案 0 :(得分:2)

答案是:是的,任何复杂程度的递归都可以以迭代的形式重写。

一般模型是我们可以通过删除每个函数中的递归调用并在while循环中将条件和逻辑移到函数外部来替换这种任意复杂的递归行为,并确保新参数传递到每个新的调用功能。我将在这里提供示例。

琐碎案例

对于琐碎的案例,def foo(): foo()这意味着我们将按如下方式更改代码:

def foo():
    pass

while True:
    # we have no variables to pass or assign
    foo()

我们现在有一个无限迭代的循环,而不是Python的有限递归。

复杂案例

对于更复杂的情况,我们也这样做,但while循环包含foo函数的条件退出。更改的参数为item,并且仍由随机调用的函数设置。我们将while循环包装在一个函数中,以便我们可以像调用foo

一样调用它
import random

MAX = 10000

def foo(my_list, item):
    my_list.append(item)
    return 'foo'

def bar(my_list, item):
    my_list.append(item[::-1])
    return 'bar'

def baz(my_list, item):
    my_list.append(item)
    return 'baz'

FUNCS = (foo, bar, baz)

def init(my_list, item='init!'):
    #     same condition   ---  item != 'foo' since we'd only stop recursing there.
    while len(my_list) < MAX or item != 'foo': 
        # Note `item` is both passed to and set by the function
        item = random.choice(FUNCS)(my_list, item)
    return my_list

''.join(init([], 'init!'))[:50]

不减小我们的MAX值,返回:

'init!zabrabbarbazzabbarbazbazzabbarfoozabbarbazbaz'

这不太优雅,因为我们需要在需要重复的动作之外添加控制流,我们不能简单地深入到递归函数中。但是,我们的堆栈跟踪要短得多。如果我们有错误,我们可能会在确定其来源时遇到困难。

<强>结论

更改是或多或少可读和可维护是值得商榷的。然而,对于大型循环任务,在Python中,使用迭代模型构建代码是唯一的方法。