问题描述
我在学校遇到了这样的问题,对此我不敢理crack。
假设我具有此功能:
def twice(f):
return lambda x: f(f(x))
好吧,我认为它的作用是将x应用于函数f,然后将结果再次应用于f。足够公平。
然后,我被要求评估以下2条陈述:
print(twice(twice)(twice(lambda x: x+3))(2))
print(twice(twice)(twice)(lambda x: x+3)(2))
如果我无法提供任何思考过程,请原谅我,因为尝试评估它们后,我对这两个完全不知所措。根据Python的评估,我知道第一个语句显示26,第二个语句显示50。我向教授寻求帮助,但他告诉我的只是“提示,请注意括号的开头和结尾”。我迷失的主要是如何在给定括号的情况下扩展“两次”功能。
在哪种情况下,(twice(lambda x: x+3))
和(twice)(lambda x: x+3)
有什么区别?有人对我如何追踪这种报价有任何建议吗?
我们非常感谢您的帮助,在此先感谢您。
解决方法
在哪种情况下,
(twice(lambda x: x+3))
和(twice)(lambda x: x+3)
有什么区别?
上下文。您错误地使它们(或更确切地说是后者)脱离了上下文。在没有上下文的情况下,这两个代码段的含义相同,即twice(lambda x: x+3)
,但没有多余的括号。
您基本上看到了f(g(x))
和f(g)(x)
,省略了f
,并询问(g(x))
和(g)(x)
之间的区别。错了在上下文中,它们分别表示f(g(x))
和(f(g))(x)
。请注意我在后者上添加的额外括号。前者计算g(x)
,然后对其应用f
。后者计算f(g)
,然后将其应用于x
。 (g)(x)
从未真正存在过。
因此,在考虑(twice)(lambda x: x+3)
时,您已经迷路了。那只是发生的事情的一部分。实际写在该函数左边的函数将首先应用于twice
,然后将所得函数应用于lambda x: x+3
。
使用function composition表示法,我们可以写两次(f)=f∘f和(f∘g)(h)= f(g(h)),从而将函数重写为更简单的形式。
第一个:
两次(两次)(两次(add3))
=(两次∘两次)(两次(add3))
=两次(两次(两次(add3)))
所以两次两次,即八次,就会应用add3。
第二个:
两次(两次)(两次)(add3)
=(两次∘两次)(两次)(add3)
=两次(两次(两次))(add3)
=两次(两次∘两次)(add3)
=(((两次∘两次)∘(两次∘两次))(add3)
=(两次∘两次∘两次))(add3)
=两次(两次(两次(两次(add3))))
所以两次两次两次,即16次,就应用了add3。
,如果您定义twice
函数以打印出一些日志记录并使lambda显式显示,则更容易查看路径:
def twice(f):
def f2(x):
res = f(f(x))
print(f"{f.__name__}({f.__name__}({x})) = {res}")
return res
return f2
def add3(x):
return x+3
您的第一个示例采用一个添加3
的函数,并创建一个添加6
的函数。然后,它创建一个函数,将其运行四次并将其应用于数字2
:
>>> twice(twice)(twice(add3))(2)
twice(twice(<function twice.<locals>.f2 at 0x7fa72f080310>)) = <function twice.<locals>.f2 at 0x7fa72f0804c0>
add3(add3(2)) = 8
add3(add3(8)) = 14
f2(f2(2)) = 14
add3(add3(14)) = 20
add3(add3(20)) = 26
f2(f2(14)) = 26
f2(f2(2)) = 26
26
您的第二个示例采用一个函数运行给定函数两次,并创建一个函数运行给定函数四次。然后,它将创建一个运行给定函数十六次的函数,然后将该函数应用于添加了3
的函数,然后将该函数应用于数字2
:
>>> twice(twice)(twice)(add3)(2)
twice(twice(<function twice at 0x7fa72f080160>)) = <function twice.<locals>.f2 at 0x7fa72f080310>
twice(twice(<function add3 at 0x7fa72f0803a0>)) = <function twice.<locals>.f2 at 0x7fa72f080280>
twice(twice(<function twice.<locals>.f2 at 0x7fa72f080280>)) = <function twice.<locals>.f2 at 0x7fa72f0801f0>
f2(f2(<function add3 at 0x7fa72f0803a0>)) = <function twice.<locals>.f2 at 0x7fa72f0801f0>
add3(add3(2)) = 8
add3(add3(8)) = 14
f2(f2(2)) = 14
add3(add3(14)) = 20
add3(add3(20)) = 26
f2(f2(14)) = 26
f2(f2(2)) = 26
add3(add3(26)) = 32
add3(add3(32)) = 38
f2(f2(26)) = 38
add3(add3(38)) = 44
add3(add3(44)) = 50
f2(f2(38)) = 50
f2(f2(26)) = 50
f2(f2(2)) = 50
50
通过添加方括号并查看结果相同,我们可以更好地了解评估顺序:
>>>>( twice(twice)(twice) ) (add3)(2)
....
50
这有点伤了我的大脑。