这个问题需要我确定以下代码的输出。
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 即使谓词不是 True。
我不明白的是 x>5 之后,当 pred 为 True 时, 为什么函数不以 if pred 结尾: 是因为没有返回值吗?即使我把返回then_clause或else_clause它仍然是一个无限循环。
我无法在 pythontutor 上测试这一点,因为它是无限递归。 谢谢你的时间。
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))
请注意,对于then_func
或else_func
,对我来说lambda
有点奇怪,因为我们根本不关心或使用它们的返回值。lambda
函数始终返回其表达式的结果。在这种情况下,这实际上是无害的,因为无论如何,print
和p
都会返回None
,这与如果我们没有明确地从常规(非lambda
)函数中return
,Python会为我们返回的内容相同。但至少对我来说,当返回值有意义时,使用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
),但在更复杂的情况下可能是不可能的。
还值得注意的是,在调用new_if
之前,2*x
的评估会立即在p
函数的作用域中进行。如果乘法是else_func
逻辑中昂贵的部分,那可能会有问题(您希望将工作推迟到实际调用else_func
的时间)。
你从自身调用函数,这会导致无限循环,你没有什么可以停止函数的。
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)
这将解决它