基于 Python 中括号的函数复合

Compounding of functions based on brackets in Python

我在学校遇到过这样的问题,我正在苦思冥想。

假设我有这个功能:

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)有什么区别?有没有人对我如何追踪这种报价有任何建议?

非常感谢任何帮助,在此先感谢您。

In which case, what difference does it make between (twice(lambda x: x+3)) and (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。

第二个:

两次(两次)(两次)(加3)
=(两次∘两次)(两次)(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

你的第二个例子是采用一个运行给定函数两次的函数,并创建一个运行给定函数四次的函数。然后它创建一个函数,该函数运行给定函数 sixteen 次,然后将其应用于添加 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

这有点伤我的脑子。