Python 如何打印递归求值过程?

Python 如何打印递归求值过程?,python,Python,下面是一个计算斐波那契数的函数: def fib(n): if n in (0, 1): return n else: return fib(n - 1) + fib(n - 2) 例如,fib(3),可以作为 # I want this block as an output!! fib(3) = fib(2) + fib(1) = (fib(1) + fib(0)) + fib(1) = (1 + 0) + fib(1) = 1 + fib(1

下面是一个计算斐波那契数的函数:

def fib(n):
    if n in (0, 1):
        return n
    else:
        return fib(n - 1) + fib(n - 2)
例如,fib(3),可以作为

# I want this block as an output!!
fib(3)
= fib(2) + fib(1)
= (fib(1) + fib(0)) + fib(1)
= (1 + 0) + fib(1)
= 1 + fib(1)
= 1 + 1
= 2

在这里,我的问题是“有可能获得这个块(方程组)作为输出吗?”我认为使用回溯模块是可能的,但没有想出任何好的方法。输出不必完全采用这种格式。如果我能得到任何类似的形式,我很高兴。

一个简单的方法,不需要任何内省或装饰,就能让球滚动:将它烘焙到函数本身中:

def format_f(f):
    if isinstance(f, int) or '+' not in f:
        return "{0}".format(f)
    return "({0})".format(f)

def fib(n, first=True):
    if first:
        yield "fib({0})".format(n)
    if n < 2:
        yield n
    else:
        yield "fib({0}) + fib({1})".format(n-1, n-2)
        for f1 in fib(n-1, False):
            yield "{0} + fib({1})".format(format_f(f1), n-2)
        for f2 in fib(n-2, False):
            yield "{0} + {1}".format(f1, format_f(f2))
        yield f1 + f2

这样做的缺点是,您必须访问
fib(n)
的实际结果值,如
list(fib(n))[-1]

在这种情况下生成不可接受代码的最简单方法可能是遵循并修改函数本身。您可以使用一个helper函数扩展他的解决方案,该函数包装了上一个函数,以便将跟踪打印为副作用,并且只返回实际的斐波那契:

def fibonacci(n):
    generator = fib(3)
    final_answer = 0
    for s in fib(3):
        print(s)
        final_answer = s
return int(final_answer)
<>但是如果你已经考虑到包装功能,那么你应该真正考虑装饰者。装潢师其实只不过是个普通人。正确构造的装饰器将允许轻松地向任何函数添加排序跟踪。这里是第一次尝试一个可能的解决方案(输出没有您想要的那么好,但它确实很好地显示了递归调用深度):

输出的示例:

>>> fac(4)
          fac(1)=    1
        *_______________
        fac(2)=    2
      *_______________
      fac(3)=    6
    *_______________
    fac(4)=   24
24
由于您正在处理斐波那契数,您可能需要考虑,即保存以前计算的值。朴素的递归实现在其他方面是相当浪费的,因为两个递归调用会多次计算许多值。你可以作为一个装饰器来实现记忆化——有一个特别好的装饰器,他们声称它可能是最快的记忆化装饰器。但是我们可以为单个(可散列)参数的函数实现一个非常简单的函数:

def simple_memoize(func):
    cache = dict()

    @functools.wraps(func)
    def wrapper(arg):
        try:
            return cache[arg]
        except KeyError:
            cache[arg] = func(arg)
            return cache[arg]

    return wrapper

>让我们看看没有记忆的斐波那契函数的迹:

@trace_recursive('+')       
def fib(n):
    if n in (0, 1):
        return n
    else:
        return fib(n - 1) + fib(n - 2)
给你:

>>> print fib(5)
            fib(1)=    1
            fib(0)=    0
          +_______________
          fib(2)=    1
          fib(1)=    1
        +_______________
        fib(3)=    2
          fib(1)=    1
          fib(0)=    0
        +_______________
        fib(2)=    1
      +_______________
      fib(4)=    3
          fib(1)=    1
          fib(0)=    0
        +_______________
        fib(2)=    1
        fib(1)=    1
      +_______________
      fib(3)=    2
    +_______________
    fib(5)=    5
5
你可以清楚地看到,每个较小的斐波那契数都被计算了多次。如果我们添加了备忘录,那么我们会得到更好的结果:

@trace_recursive('+')
@simple_memoize
def fib(n):
    if n in (0, 1):
        return n
    else:
        return fib(n - 1) + fib(n - 2)
输出:

>>> print fib(5)
            fib(1)=    1
            fib(0)=    0
          +_______________
          fib(2)=    1
          fib(1)=    1
        +_______________
        fib(3)=    2
        fib(2)=    1
      +_______________
      fib(4)=    3
      fib(3)=    2
    +_______________
    fib(5)=    5
5
>>> print fib(5)
            fib(1)=    1
            fib(0)=    0
          +_______________
          fib(2)=    1
        +_______________
        fib(3)=    2
      +_______________
      fib(4)=    3
    +_______________
    fib(5)=    5
5
顺便说一句:装饰顺序通常是非交换的。每个decorator都包装了它所在的整个装饰功能,即最上面的decorator是最外面的。我们可以在示例中看到这一点:

@simple_memoize
@trace_recursive('+')
def fib(n):
    if n in (0, 1):
        return n
    else:
        return fib(n - 1) + fib(n - 2)
输出:

>>> print fib(5)
            fib(1)=    1
            fib(0)=    0
          +_______________
          fib(2)=    1
          fib(1)=    1
        +_______________
        fib(3)=    2
        fib(2)=    1
      +_______________
      fib(4)=    3
      fib(3)=    2
    +_______________
    fib(5)=    5
5
>>> print fib(5)
            fib(1)=    1
            fib(0)=    0
          +_______________
          fib(2)=    1
        +_______________
        fib(3)=    2
      +_______________
      fib(4)=    3
    +_______________
    fib(5)=    5
5

尽管在这两种情况下,回忆录都同样有效,但在内部回忆录情况下,在回忆录功能快捷方式到缓存之前打印跟踪,而在外部回忆录情况下,缓存的快捷方式完全阻止了内部调用,因此永远不会调用跟踪修饰符。

这可能是修饰符的好地方。您肯定可以得到输出块,问题实际上是helper函数是什么样子的。对于n==4,它会是什么样子?您希望何时打印fib(n)的实际值而不是“fib(n)”?仅当n达到1或0时?另外,在计算fib(n)后打印块是否可以,或者在计算过程中是否必须打印块?
>>> print fib(5)
            fib(1)=    1
            fib(0)=    0
          +_______________
          fib(2)=    1
        +_______________
        fib(3)=    2
      +_______________
      fib(4)=    3
    +_______________
    fib(5)=    5
5