仅在必要时评估Python递归

时间:2018-11-03 13:06:05

标签: python python-3.x

说我有这两个功能:

def s(x,y,z):
   if x <= 0:
       return y
   return z

def f(a,b):
    return s(b, a+1, f(a,b-1)+1)

如果我想在脑海中找到f(5,2),它将像这样:

f(5,2) = s(2,6,f(5,1)+1)
f(5,1) = s(1,6,f(5,0)+1)
f(5,0) = s(0,6,f(5,-1)+1) = 6
f(5,1) = 7
f(5,2) = 8

我从不评估f(5,-1),因为不需要它。 s函数将返回6,因为参数x为零,因此不需要对参数z进行求值。

但是,如果我尝试在python中运行它,它将一直循环递归,或者直到我得到最大递归深度错误为止,这大概是因为python想在执行s函数之前评估所有参数。

我的问题是,如何以不再需要递归的方式实现这些功能或任何类似方案?是否可以将每个参数的求值时间推迟到函数中使用?

2 个答案:

答案 0 :(得分:4)

您的思想正在与s()的工作原理有关的“内幕知识”。 Python不能,因此它只能遵循严格的规则,即在进行调用之前必须对调用的所有参数表达式进行求值。

Python是一种高度动态的语言,在执行的每个步骤中,sf都可以反弹以指向另一个对象。这意味着Python无法优化递归或内联函数逻辑。它无法将if x <= 0的测试s()取消以避免避免首先评估z的值。

如果您作为程序员知道在某些情况下需要避免使用第三种表达式,则需要自己进行此优化。手动将s中的逻辑合并到f中:

def f(a, b):
    if b <= 0:
        return a + 1
    return f(a, b - 1) + 1

或通过传入一个可调用的变量并使s()负责对第三表达式的求值,直到s确定是否需要计算它为止。

def s(x, y, z):
   if x <= 0:
       return y
   return z()   # evaluate the value for z late

def f(a, b):
    # make the third argument a function so it is not evaluated until called
    return s(b, a+1, lambda: f(a, b - 1) + 1)

答案 1 :(得分:3)

调用函数时,所有参数在传递给函数之前都经过充分评估。换句话说,f(5,-1)甚至在s开始之前就被执行。

幸运的是,有一种简单的方法可以按需评估表达式:函数。与其将f(a,b-1)结果传递给z,而是将其传递给计算结果的 function

def s(x,y,z):
   if x <= 0:
       return y
   return z()  # z is a function now

def f(a,b):
    return s(b, a+1, lambda:f(a,b-1)+1)

print(f(5,2))  # output: 8