这是一个计算Fibonacci数的函数:
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
我的问题是“是否有可能将此块(方程组)作为输出?”我认为使用traceback模块可能有可能,但无法想出任何好方法。输出不必完全采用这种格式。如果我能得到任何类似的表格,我很高兴。
答案 0 :(得分:3)
一种简单的方法,没有任何内省或装饰,让球滚动:将其烘焙到功能本身:
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
使用示例:
>>> for s in fib(3):
print(s)
fib(3)
fib(2) + fib(1)
(fib(1) + fib(0)) + fib(1)
(1 + fib(0)) + fib(1)
(1 + 0) + fib(1)
1 + fib(1)
1 + 1
2
>>> for s in fib(4):
print(s)
fib(4)
fib(3) + fib(2)
(fib(2) + fib(1)) + fib(2)
((fib(1) + fib(0)) + fib(1)) + fib(2)
((1 + fib(0)) + fib(1)) + fib(2)
((1 + 0) + fib(1)) + fib(2)
(1 + fib(1)) + fib(2)
(1 + 1) + fib(2)
2 + fib(2)
2 + (fib(1) + fib(0))
2 + (1 + fib(0))
2 + (1 + 0)
2 + 1
3
这样做的缺点是您必须访问fib(n)
的实际结果值,如list(fib(n))[-1]
。
答案 1 :(得分:0)
在这种情况下生成不可修改代码的最简单方法可能是遵循@jonrshapre并修改函数本身。您可以使用辅助函数扩展其解决方案,包装上一个函数,以便将跟踪打印为副作用,并且仅返回实际的Fibonacci:
def fibonacci(n):
generator = fib(3)
final_answer = 0
for s in fib(3):
print(s)
final_answer = s
return int(final_answer)
但是如果你已经考虑过包装函数,那么你应该考虑装饰器。装饰者实际上只不过是syntactic sugar for wrapping other functions。正确构造的装饰器将允许轻松地向任何功能添加一系列排序。这是第一次尝试一种可能的解决方案(输出不如你想要的那样好,但它确实显示了递归调用深度):
import functools
def trace_recursive(combine,line_length=15):
def dec_wrapper(func):
@functools.wraps(func)
def func_wrapper(*args,**kargs):
positional = [str(a) for a in args]
keyword = ["{}={}".format(k, kargs[k]) for k in kargs]
call = ', '.join(positional + keyword)
call = "{}({})".format(func.__name__, call)
func_wrapper.depth = func_wrapper.depth + 1
func_wrapper.last_depth = func_wrapper.depth
return_val = func(*args,**kargs)
if func_wrapper.depth < func_wrapper.last_depth:
print "{}{}{}".format(func_wrapper.depth*' ',combine,line_length*'_')
call_and_value = "{}={:>5}".format(call,return_val)
indent = func_wrapper.depth * ' '
call_and_value = indent + call_and_value
print call_and_value
func_wrapper.depth = func_wrapper.depth - 1
return return_val
func_wrapper.depth = 1
func_wrapper.last_depth = 1
return func_wrapper
一旦有了装饰器,就可以轻松地将它应用于任何功能:
@trace_recursive('*')
def fac(n):
if n in(0, 1):
return 1
else:
return n * fac(n-1)
输出样本:
>>> fac(4)
fac(1)= 1
*_______________
fac(2)= 2
*_______________
fac(3)= 6
*_______________
fac(4)= 24
24
由于您正在处理斐波那契数字,您可能需要考虑memoization,即保存先前计算的值。由于双重递归调用,天真的递归实现非常浪费,多次计算多个值。您可以将memoization实现为装饰器 - 有一个whole section at the Python Decorator Library和Active State有一个特别好的,他们声称这可能是最快的memoization装饰器。但是我们可以为单个(可散列)参数的函数实现一个非常简单的参数:
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
那么,让我们看一下没有记忆的Fibonacci函数的跟踪:
@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
你可以清楚地看到每个较小的斐波纳契数都是多次计算的。如果我们添加memoization,那么我们会得到更好的结果:
@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
BTW:装饰者顺序一般来说是不可交换的。每个装饰器包裹它坐在上面的完整装饰功能,即最上面的装饰器是最外面的。我们可以在我们的示例中看到这一点:
@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(3)= 2
+_______________
fib(4)= 3
+_______________
fib(5)= 5
5
虽然在两种情况下,memoization都同样有效,但在内部memoization情况下,跟踪在memoization函数快捷方式缓存之前打印,而在外部memoization情况下,缓存的快捷方式完全阻止内部调用,以便永远不会调用跟踪装饰器。