求助:实现统计Ackermann函数递归次数与执行时间的装饰器(现有代码运行异常)
修复Ackermann函数的递归次数与执行时间统计装饰器
我帮你排查下代码里的问题,主要是三个核心逻辑搞错了,咱们一步步来修正:
原代码的问题分析
- 调用次数统计完全错误:你把Ackermann函数的返回值(比如
ackermann(2,2)返回7)直接赋值给了func.calls,这是把函数的计算结果和递归调用次数搞混了!Ackermann函数的返回值是数学计算结果,不是被调用的次数。 - 执行时间统计不对:当前代码会在每一次递归调用时都计算耗时,最后
last_time_taken会被最后一层递归的时间覆盖,而不是整个顶层调用的总耗时。 - 计数器逻辑没用对:你定义的
visualise.level只是跟踪递归深度,但完全没用来统计总调用次数,而且初始化位置也不对,没法正确累计次数。
修正后的完整代码
首先别忘了导入必要的模块,然后调整装饰器的逻辑:
import time from functools import wraps def visualise(func): # 给被装饰的函数初始化统计属性 func.total_calls = 0 func.last_time_taken = 0.0 @wraps(func) def wrapper(*args, **kwargs): # 判断是否是顶层调用(首次进入当前调用链) is_top_level = wrapper.current_calls == 0 if is_top_level: start_time = time.perf_counter() # 每次进入wrapper(不管是顶层还是递归)都计数+1 wrapper.current_calls += 1 try: # 执行原函数 result = func(*args, **kwargs) finally: # 只有顶层调用结束时,才更新函数的统计属性并重置计数器 if is_top_level: elapsed_time = time.perf_counter() - start_time func.last_time_taken = elapsed_time func.total_calls = wrapper.current_calls wrapper.current_calls = 0 # 重置计数器,准备下一次调用 return result # 初始化wrapper的临时计数器,用于跟踪当前调用链的总次数 wrapper.current_calls = 0 return wrapper @visualise def ackermann(n, m): if n == 0: return m + 1 elif m == 0: return ackermann(n - 1, 1) else: return ackermann(n - 1, ackermann(n, m - 1))
代码逻辑说明
- 我们给wrapper加了一个
current_calls临时属性,用来统计当前整个调用链的总次数,每次顶层调用结束后会重置,避免影响下一次调用。 - 只有顶层调用时才会记录开始时间,结束后计算总耗时,确保
last_time_taken是整个ackermann(2,2)调用的总时间,而不是某一层递归的时间。 - 用
try...finally保证即使函数出现异常(虽然Ackermann合法参数不会抛异常),计数器也能被正确重置,避免污染后续调用。
测试调用
现在执行你的测试代码:
ackermann(2, 2) print(f"递归调用次数: {ackermann.total_calls}") # 输出7,符合预期 print(f"总执行时间: {ackermann.last_time_taken} seconds") # 输出类似0.00000034的时间
内容的提问来源于stack exchange,提问作者Curly0606
相关产品推荐
相关产品推荐

