这个问题要求我确定以下代码的输出。
def new_if(pred, then_clause, else_clause):
if pred:
then_clause
else:
else_clause
def p(x):
new_if(x>5, print(x), p(2*x))
p(1)
我不明白为什么它会是一个无限循环。 输出1,2,4,8,16 ....等等。
据我所知,将print(x)作为参数传递即可 直接打印x,这就是为什么输出有1,2,4,即使谓词不是真的。
我不理解的是在x> 5之后,当pred为True时, 为什么函数不会在if pred结束: 是因为没有回报价值吗?即使我把return_clause或else_clause放回去,它仍然是一个无限循环。
我无法在pythontutor上测试这个,因为它是无限递归。 感谢您的时间。
答案 0 :(得分:1)
你正在调用自身的函数,这会导致无限循环而你没有任何东西可以阻止这个函数。
def new_if(pred, then_clause, else_clause):
if pred:
then_clause
else:
else_clause
def p(x):
if x<5:
new_if(x>5, print(x),p(2*x))
p(1)
这将解决它
答案 1 :(得分:1)
Python不允许将x > 5
之类的表达式作为代码传递给其他函数(至少,不是直接代码试图这样做)。如果您调用foo(x > 5)
之类的函数,则会在调用者的范围内立即评估x > 5
表达式,并且只将评估的结果传递给正在运行的函数调用。其他函数调用中的函数调用也会发生同样的情况。当Python看到foo(bar())
时,它会先调用bar
,然后使用foo
的返回值调用bar
。
在p(x)
函数中,代码正在尝试将p(2*x)
传递给new_if
函数,但自new_if
以来,解释器永远不会到达p
调用会一直保持递归(或者更确切地说,直到超出最大递归深度引发异常)。
使代码工作的一种方法是将表达式放入lambda
函数,并更改new_if
来调用它们。将代码捆绑到函数中可以延迟表达式的求值,直到调用函数,并且没有无限递归,因为pred_func
通常会在某个时刻返回True
(虽然如果你打电话给p(0)
或p(-1)
,它仍然可以永远递归:
def new_if(pred_func, then_func, else_func):
if pred_func():
then_func()
else:
else_func()
def p(x):
new_if(lambda: x>5, lambda: print(x), lambda: p(2*x))
请注意lambda
对then_func
或else_func
感到有点奇怪,因为我们根本不关心或使用它们的返回值。 lambda
函数始终返回其表达式的结果。在这种情况下,这实际上是非常无害的,因为无论如何print
和p
都会返回None
,这与Python为我们返回的内容相同,如果我们没有这样做的话。从常规(非return
)函数中明确lambda
。但至少对我来说,当返回值意味着什么时,使用lambda
似乎更自然。 (也许new_if
应该return
从它调用的函数返回的值?)
如果您不想编写闭包(即必须在封闭范围内查找x
的函数),您可以使用functools.partial
将预先计算的参数绑定到函数之类的print
和p
没有立即调用这些函数。例如:
from functools import partial
def p(x):
return new_if(partial((5).__lt__, x), partial(print, x), partial(p, 2*x))
仅当每个表达式都可以转换为对现有函数的单个调用时,此方法才有效。在这种情况下可以完成(对pred_func
有一点创造性和细致的语法),但在更复杂的情况下可能无法实现。
值得注意的是,在2*x
调用p
之前,new_if
范围内的else_func
评估会立即发生。如果该乘法是else_func
逻辑的昂贵部分,则可能会出现问题(您希望将工作推迟到实际调用{{1}}时)。